`DEFAULT_PROMPT_PATH` custom instructions are silently dropped on a non-UTF-8 locale
Severity: Medium
Type: Correctness / Silent data loss
File: agent/prompt.py:21-48
Description
_load_default_prompt() has two branches that read the same kind of file. One passes an explicit
encoding; the other does not:
def _load_default_prompt() -> str:
try:
if DEFAULT_PROMPT_PATH:
content = Path(DEFAULT_PROMPT_PATH).read_text().strip() # no encoding
else:
content = (
resources.files("agent.resources")
.joinpath("default_prompt.md")
.read_text(encoding="utf-8") # encoding
.strip()
)
...
except Exception:
logger.warning(
"Failed to read default prompt from %s",
DEFAULT_PROMPT_PATH or "agent.resources/default_prompt.md",
)
return ""DEFAULT_PROMPT_PATH is an operator-supplied path (os.environ.get("DEFAULT_PROMPT_PATH")).
Impact
An operator who points DEFAULT_PROMPT_PATH at a UTF-8 prompt file containing any non-ASCII
character — a curly quote, an em dash, an arrow, an emoji — gets a UnicodeDecodeError on any
host whose locale encoding is not UTF-8. The bare except Exception swallows it, logs a single
warning, and returns "".
The result is that the agent runs with the operator's custom instructions silently missing. There is no error surfaced to the caller and no difference in agent behaviour other than the instructions being absent, which is close to the worst possible failure mode for a configuration knob: the deployment looks healthy and the agent quietly ignores its custom prompt.
This is distinct from the general missing-encoding= issue I filed alongside this one: that one is a
test/portability problem, this one changes production agent behaviour.
Suggested fix
Add encoding="utf-8" to the DEFAULT_PROMPT_PATH branch so both branches match:
content = Path(DEFAULT_PROMPT_PATH).read_text(encoding="utf-8").strip()Separately, consider narrowing the except Exception or raising when DEFAULT_PROMPT_PATH was
explicitly set: an operator who configured a prompt file almost certainly wants a hard failure
over a silent fallback to "no custom instructions".
Found during a full-repo audit (ruff + basedpyright + pytest on a Windows host, plus manual review). Filing each finding separately so they can be triaged independently.
Source: langchain-ai/open-swe