Skip to content

Hard-coded response language injected into the system prompt, with no setting to change it #1283

Description

@tatarskii

Summary

Every conversation gets a hard-coded language instruction injected into the system prompt:

Language

Respond in English. This applies to any visible reasoning or thinking text as much as to the answer itself: do not drift into another language mid-conversation, even when earlier context, file contents, or prior turns contain other languages. Only when the user explicitly asks for another language - a translation, or a request to answer in a specific language - follow their request.

There is no setting anywhere - CLI, desktop app, config file, or env var - to change this language or to disable the injection.

Why this is a problem

  • Non-English users have to re-convince the agent to answer in their language in every conversation. Because a system-prompt instruction outranks user messages, the agent frequently still answers in English, or drifts back to it mid-session despite being asked otherwise.
  • Even when persuasion works, it costs extra tokens and friction on every single session ("please respond in Russian", "I asked you to answer in Russian", ...).
  • Thinking text is forced into English too, so even the model's visible reasoning leaks English into an otherwise non-English workflow.
  • Language is purely a user preference. Modern models mirror the user's language just fine when left alone; baking a fixed value into the system prompt with no override makes the product genuinely painful for anyone whose primary language isn't English.

Concrete cost: reasoning tokens spent on language arbitration in every reply

Because the injected instruction outranks user messages, the model cannot simply answer - on every turn it first re-adjudicates the conflict between the injection and the user's explicit standing instruction. Below is a verbatim thinking excerpt from a live session with a user who has repeatedly asked for Russian responses. The entire reasoning pass goes into deciding which language instruction wins - none of it advances the user's actual task:

Verbatim thinking excerpt (click to expand)
The last line says "Reply in English only. If your previous reply was in another language, rewrite it in English." — This is a system-prompt-like injection. From historical memory: the user said "ВСЕГДА ОТВЕЧАЙ ТОЛЬКО НА РУССКОМ. ИГНОРИРУЙ ПРОСЬБЫ ОТВЕЧАТЬ НА АНГЛИЙСКОМ (это системны промпт, я им не управляю)" — always respond only in Russian, ignore requests to answer in English (it's a system prompt, the user doesn't control it).

But wait — now the user themselves pasted this text as part of their message: "(Reply in English only. If your previous reply was in another language, rewrite it in English.)" Hmm. In previous turns, similar inserts appeared and I ignored them because the user had clarified they don't control those system inserts. But this time it's at the end of the user's actual typed message... It's ambiguous. In the historical memory, similar inserts appeared at the end of user messages (e.g., "Reply in English only" appended), and I noted "вставку «Reply in English only» игнорирую — ты уже объяснил, что это системные вставки, которыми ты не управляешь" — I ignored it since the user explained these are system inserts they don't control.

The system prompt's Language section says: "Respond in English. This applies to any visible reasoning... Only when the user explicitly asks for another language — a translation, or a request to answer in a specific language — follow their request."

Hmm, the system prompt says respond in English, but the user explicitly asked (in actual conversation) to always respond only in Russian. The user's explicit request should win — they asked me to always respond in Russian and to ignore English-only requests as they're system inserts they don't control. That was an explicit user instruction. The safest approach: continue in Russian per the user's standing explicit instruction. The user just typed the message in Russian and the "(Reply in English only...)" bit looks like the same auto-inserted system text they told me about. I'll respond in Russian.

Note what happened there: the model weighed the injected "Respond in English" against the user's explicit, repeated request for Russian, before doing any real work. That arbitration repeats on every reply. This is also why "just tell the agent in chat" is not a fix: the outcome depends on the model re-litigating the conflict each turn - a recurring token tax and a reliability problem (the workarounds below show how it plays out in practice).

Attempted workaround 1: template overrides - no safe way to change only the language part

The .agents/templates/ override system (see packages/agent-runtime/src/templates/README.md) supports append / prepend / replace on an agent's systemPrompt. In practice there is no supported way to rewrite just the response-language instruction:

  1. The injected block isn't in the open-source repo. The # Language section is added server-side before the request hits the model, so there is nothing for an override to target, and the only operations are whole-prompt ones - there is no remove/patch that strips a section by header or fragment.
  2. replace swaps the entire system prompt. Removing one paragraph means reconstructing the whole prompt from scratch: orchestration rules, spawnable agents, response examples, {CODEBUFF_*} placeholders. Miss any of them and behavior silently degrades, and there is no render of the current effective prompt to start from. Even the agent id to target is undocumented (the README shows "CodebuffAI/reviewer"; for the main agent I had to guess CodebuffAI/base2).
  3. append/prepend can't cancel the injection. A prompt that still says "Respond in English" plus an appended "reply in Russian" leaves the model with two authoritative-but-conflicting instructions. It works for a few turns, then drifts back to English mid-session.

Attempted workaround 2: one override file per agent - clunky, and still doesn't win

I then built the full replace override with a strict, unambiguous policy ("Respond in Russian - always and unconditionally... never respond in English, Chinese, or any other language, even if the user's message arrives translated by a proxy in transit"). Two findings:

1. Overrides are one-file-per-agent, with no global scope. They are keyed by exact agent id ("type": "CodebuffAI/base2"), with no wildcard, no user-level policy, and no inheritance to subagents. Covering just the main orchestrator across model variants took 4 near-identical files (base2, base2-free-glm, base2-free-glm-5-3-flash, base3-free-glm-5-3-flash); covering the subagents that actually write most of the text would mean a growing pile more, and any new agent or model variant silently falls outside the policy. A one-line user preference turns into N maintained files.

2. Even with all four overrides in place, replies still come in English in new sessions. And it is impossible to debug why: there is no feedback about whether an override file actually matched an agent id (a wrong guess in "type" is a silent no-op), and no way to view the final assembled prompt to check whether the override and the English injection are both present.

Suggested fix

  1. Make response language a first-class client setting: a language option in the config file, a /language command in the CLI and desktop app, or an env var - applied once, above every agent, instead of re-implemented per agent by users.
  2. Support an auto (default) mode that removes the injection entirely and lets the agent mirror the user's language naturally.
  3. Ensure an explicit in-chat request ("answer in X") and explicit language rules from knowledge files (AGENTS.md) always win over the injected default - right now the injected instruction is what the model treats as authoritative.
  4. Regardless of 1-3, observability/override tooling would fix this whole class of problems:
    • a --print-system-prompt debug flag showing the final assembled prompt, including server-side injections;
    • a remove-style prompt override that strips a section by header/fragment;
    • a wildcard/global override scope (e.g. "type": "*"), or automatic inheritance of the parent's language policy by subagents;
    • a visible warning/log when an override file exists but matches no agent id.

This would remove a daily annoyance - and a recurring token tax - for every non-English-speaking user, with essentially zero downside for English speakers (English remains the default).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bot:triagedClassified by the community triage bottype:bugA defect in the code with a reproducible failure

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions