2025, Dec 18 17:00

Stop \x3a in URLs from .env: make Python environment variables consistent with python-dotenv override=True

Troubleshoot hex-encoded URLs from .env in Python. Fix \x3a issues by loading environment variables with python-dotenv override=True for consistent config.

Hex sequences inside a URL coming from .env can ruin an otherwise simple config flow. The case looks especially strange when the same .env value sometimes prints as a clean URL and sometimes as one with \x3a replacing colons. Here is a concise walkthrough of what’s going on and how to make your environment variables load consistently in Python.

Reproducing the odd URL from .env

Start with a straightforward environment entry:

GATEWAY_URL="http://localhost:9000"

Load it in Python via python-dotenv and read it with os.getenv. Even an extra unquote pass doesn’t help in this scenario:

import os
from urllib.parse import unquote
from dotenv import load_dotenv

load_dotenv()

service_host = os.getenv("GATEWAY_URL", "")
print(service_host)

service_host = unquote(os.getenv("GATEWAY_URL", ""))
print(service_host)

Both prints can look like: http\x3a//localhost\x3a9000.

Now add another entry:

NEXT_GATEWAY="http://localhost:9001"

Reading it right after editing the file may produce the normal output:

load_dotenv()

alt_host = os.getenv("NEXT_GATEWAY", "")
print(alt_host)

which can show http://localhost:9001. However, after restarting the virtual environment, it might regress and print as http\x3a//localhost\x3a9001. The URL sanitization is not the point here; the question is the inconsistency between what’s in .env and what Python receives.

What’s actually happening

The behavior is inconsistent because the value arriving in Python can sometimes be pre-encoded before python-dotenv reads your .env. In other words, the environment might already contain an altered version of the variable, and merely calling os.getenv returns that altered value. Changing quotes or manually “cleaning” the string does not address why the wrong content ends up in the environment in the first place.

The fix that made it consistent

Forcing python-dotenv to overwrite any preexisting environment variables resolves the issue in practice. This ensures that values from the .env file replace whatever might have been injected earlier:

from dotenv import load_dotenv
import os

load_dotenv(override=True)
cloud_addr = os.getenv("SUPABASE_URL")
print(cloud_addr)

With the override flag set, a previously mangled URL like

https\3a//xxxxxxxxxxxxxxxxxx.supabase.co

prints as

https://xxxxxxxxxxxxxxxxxx.supabase.co

It was also noted that this behavior ties back to a VSCode issue that has just been fixed, and forcing override got things working even on the affected setup. Details: https://github.com/microsoft/vscode/issues/248468.

Why this matters

Configuration-by-environment should be predictable across platforms and sessions. If the runtime picks up an already altered variable, you get a working URL one moment and a hex-encoded variant after a restart. That breaks assumptions in CI/CD pipelines, local development, and any reproducible environment story. Ensuring that the value loaded from .env actually wins prevents subtle, time-wasting debugging sessions.

Wrap-up

If URLs from .env sporadically show up with \x3a or \3a instead of colons, load your environment with overriding enabled. This removes inconsistencies caused by pre-populated or editor-injected values and restores a clean, predictable configuration flow. If you’re using VSCode, be aware of the linked issue; the workaround above is effective even where the bug appears. Keep the focus on making the environment authoritative and verify the effective values at startup so you catch any mismatch early.