Claude Code unexpectedly switches to Hind for 3 words in German response

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 16, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

<html><head></head><body><h1>[BUG] Language bleed: Hindi (Devanagari) phrase substituted into German output</h1>
<h2>Summary</h2>
<p>During a long German-language Claude Code session, the model began an assistant message with a <strong>Hindi phrase in Devanagari script</strong> and continued the same sentence in German. Nothing in the session, the repository, or the user's input involved Hindi or any Indic language at any point.</p>
<p>The inserted phrase is <strong>not a random token</strong>: <code>काम करता है</code> means <em>"it works"</em> — exactly what the German sentence would otherwise have started with (<em>"Es funktioniert"</em>). It is a <strong>semantic substitution</strong>: the model produced the intended meaning in the wrong script, then resumed German mid-sentence.</p>
<h2>Actual output</h2>
<pre><code>काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.
</code></pre>
<p>Translated: <em>"it works [Hindi] — the bound grips now [German]. Adjust the numbers and run the counter-check."</em></p>
<h2>Expected output</h2>
<pre><code>Es funktioniert — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.
</code></pre>
<h2>Position in the tool loop</h2>
<p>The anomaly is the <strong>first token of an assistant message immediately following a successful tool result</strong>. The model had just applied a one-line fix to a Rust source file and re-run the project's checker; the preceding tool result showed the fix had taken effect (a previously failing lookup now resolved, with only a different, expected error remaining):</p>
<pre><code>[5420] TOOL_RESULT:
ok
Fehler: [K002] beispiele/18-vorfahren.gab:43:11: der Block haelt TOPO fuer 768 ops, die Sperre sagt held &lt;= 600 ops zu
beispiele/18-vorfahren.gab: 5 Items, 1 Fehler, 0 Hinweise

[5422] assistant TEXT:
काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.
</code></pre>
<p>The full sanitised tool-call window around the anomaly (all entries, nothing omitted) is in the attached <code>claude-code-issue-language-bleed-window.md</code>.</p>
<p><strong>Shape of the anomaly:</strong> the Hindi is not inserted <em>into</em> a German clause — it occupies the slot where the German opening phrase would have been, and the German picks up cleanly after the dash. Whatever selected the script did so for one phrase-sized unit at the very start of the message. Every preceding and following assistant message in the session was in German with no anomaly.</p>
<h2>Environment</h2>

  |  
-- | --
Claude Code | 2.1.220
Model | claude-opus-5
Platform | Linux (hardened kernel), CLI in a terminal
Session language | German throughout — user input, repository prose, tool output
Session length | Very long; context compacted at least once earlier in the session
Tool usage | Heavy; many Bash/build/test cycles
Frequency | Observed once in a session of several hundred messages

<p>Project context: a Rust compiler project (<a href="https://github.com/SimonVitzethum/Gabbro">SimonVitzethum/Gabbro</a>) with German identifiers and German prose throughout. The window contains German, Rust source, shell commands and compiler output — no Hindi, no transliteration, no Unicode above Latin-1 apart from umlauts and typographic dashes.</p>
<h2>Reproduction</h2>
<p>Not reproducible on demand; observed once. Conditions:</p>
<ul>
<li>long-running German session, dense technical work (Rust, shell)</li>
<li>heavy tool use, many build/test cycles</li>
<li>at least one context compaction earlier in the session</li>
<li>occurred at a "success" moment — immediately after a fix started working</li>
</ul>
<p>If a pattern is worth grepping for in other reports, it is a <strong>short exclamation-like message-initial phrase after a success signal</strong> (<em>"it works"</em>, <em>"done"</em>, <em>"got it"</em>) — that is the position where this occurred.</p>
<h2>Relation to existing language-bleed reports</h2>
<ul>
<li><a href="https://github.com/anthropics/claude-code/issues/68628">#68628 — Spontaneous language bleed: Russian word in English output</a> — same shape: a single foreign word <strong>replacing the intended word</strong>, no trigger in context</li>
<li><a href="https://github.com/anthropics/claude-code/issues/23436">#23436 — Random word hallucination/glitching in Czech</a></li>
<li><a href="https://github.com/anthropics/claude-code/issues/57212">#57212 — Response language switches from Traditional Chinese to Korean/Japanese</a></li>
<li><a href="https://github.com/anthropics/claude-code/issues/30025">#30025 — Claude spontaneously switches to Korean mid-conversation when using Chinese</a></li>
<li><a href="https://github.com/anthropics/claude-code/issues/27362">#27362 — Chinese character artifacts appearing in output</a></li>
<li><a href="https://github.com/anthropics/claude-code/issues/32181">#32181 — Output gradually drifts to English during code-heavy work</a></li>
</ul>
<p>Most existing reports involve <strong>CJK or Cyrillic</strong>, and several involve <em>drifting</em> to another language for a whole passage. This case differs in two ways that may help narrow the cause:</p>
<ol>
<li><strong>A single short phrase</strong>, not a passage — the sentence resumes correctly in German mid-clause, so whatever produced it did not switch the response language.</li>
<li><strong>The phrase is a correct translation of the intended words</strong>, which points at script/language selection at generation time rather than corrupted or hallucinated tokens.</li>
</ol>
<h2>Impact</h2>
<p>Output correctness/reliability, not functionality or security. The output remains understandable, but the fact that the substituted phrase is semantically appropriate may indicate a language-selection issue in generation rather than simple token corruption — which is why this is filed separately rather than as a "me too" on the drift issues above.</p>
<h2>Notes</h2>
<ul>
<li>Not intentionally triggered; the user did not prompt for Hindi at any point.</li>
<li>The model itself noticed the slip when asked about it; the anomaly was limited to this one phrase.</li>
<li>The quoted message is complete; no surrounding text is omitted.</li>
<li>Happy to provide the full sanitised raw transcript if useful for investigation.</li>
</ul></body></html># [BUG] Language bleed: Hindi (Devanagari) phrase substituted into German output

Summary

During a long German-language Claude Code session, the model began an assistant message with a Hindi phrase in Devanagari script and continued the same sentence in German. Nothing in the session, the repository, or the user's input involved Hindi or any Indic language at any point.

The inserted phrase is not a random token: काम करता है means "it works" — exactly what the German sentence would otherwise have started with ("Es funktioniert"). It is a semantic substitution: the model produced the intended meaning in the wrong script, then resumed German mid-sentence.

Actual output

काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.

Translated: "it works [Hindi] — the bound grips now [German]. Adjust the numbers and run the counter-check."

Expected output

Es funktioniert — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.

Position in the tool loop

The anomaly is the first token of an assistant message immediately following a successful tool result. The model had just applied a one-line fix to a Rust source file and re-run the project's checker; the preceding tool result showed the fix had taken effect (a previously failing lookup now resolved, with only a different, expected error remaining):

[5420] TOOL_RESULT:
ok
Fehler: [K002] beispiele/18-vorfahren.gab:43:11: der Block haelt `TOPO` fuer 768 ops, die Sperre sagt `held <= 600 ops` zu
beispiele/18-vorfahren.gab: 5 Items, 1 Fehler, 0 Hinweise

[5422] assistant TEXT:
काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.

The full sanitised tool-call window around the anomaly (all entries, nothing omitted) is in the attached claude-code-issue-language-bleed-window.md.

Shape of the anomaly: the Hindi is not inserted into a German clause — it occupies the slot where the German opening phrase would have been, and the German picks up cleanly after the dash. Whatever selected the script did so for one phrase-sized unit at the very start of the message. Every preceding and following assistant message in the session was in German with no anomaly.

Environment

| | |
|---|---|
| Claude Code | 2.1.220 |
| Model | claude-opus-5 |
| Platform | Linux (hardened kernel), CLI in a terminal |
| Session language | German throughout — user input, repository prose, tool output |
| Session length | Very long; context compacted at least once earlier in the session |
| Tool usage | Heavy; many Bash/build/test cycles |
| Frequency | Observed once in a session of several hundred messages |

Project context: a Rust compiler project ([SimonVitzethum/Gabbro](https://github.com/SimonVitzethum/Gabbro)) with German identifiers and German prose throughout. The window contains German, Rust source, shell commands and compiler output — no Hindi, no transliteration, no Unicode above Latin-1 apart from umlauts and typographic dashes.

Reproduction

Not reproducible on demand; observed once. Conditions:

  • long-running German session, dense technical work (Rust, shell)
  • heavy tool use, many build/test cycles
  • at least one context compaction earlier in the session
  • occurred at a "success" moment — immediately after a fix started working

If a pattern is worth grepping for in other reports, it is a short exclamation-like message-initial phrase after a success signal ("it works", "done", "got it") — that is the position where this occurred.

Relation to existing language-bleed reports

Most existing reports involve CJK or Cyrillic, and several involve drifting to another language for a whole passage. This case differs in two ways that may help narrow the cause:

  1. A single short phrase, not a passage — the sentence resumes correctly in German mid-clause, so whatever produced it did not switch the response language.
  2. The phrase is a correct translation of the intended words, which points at script/language selection at generation time rather than corrupted or hallucinated tokens.

Impact

Output correctness/reliability, not functionality or security. The output remains understandable, but the fact that the substituted phrase is semantically appropriate may indicate a language-selection issue in generation rather than simple token corruption — which is why this is filed separately rather than as a "me too" on the drift issues above.

Notes

  • Not intentionally triggered; the user did not prompt for Hindi at any point.
  • The model itself noticed the slip when asked about it; the anomaly was limited to this one phrase.
  • The quoted message is complete; no surrounding text is omitted.
  • Happy to provide the full sanitised raw transcript if useful for investigation.

What Should Happen?

Respond in german

Error Messages/Logs

# Language bleed — full tool-call window around the anomaly

**Companion to `claude-code-issue-language-bleed.md`.** That document states the bug; this one
is the raw context, so the position of the anomaly in the tool loop can be judged rather than
taken on trust.

Sanitised: absolute paths replaced by `<repo>` / `<home>`, the account name by `<user>`.
Nothing else is altered, nothing is omitted from inside the window. Transcript entry numbers
are kept so the ordering is visible; gaps are entries that carried no text, no tool call and no
result (e.g. pure thinking blocks).

## What the model was doing

A long German-language session. The immediate work: a cost-checking pass in the project's own
compiler did not resolve a table's capacity bound, because the table name arrived unqualified
from an index type while the capacity map is keyed by qualified name. The model was reading
the relevant functions, then patched the lookup, then re-ran the checker.

**The anomaly is at entry 5422**, immediately after the tool result at 5420 — the first result
showing the patch had taken effect (`K003` gone, a different and expected `K002` remaining).

## The anomaly, in place


काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.


`काम करता है` (Hindi) = *"it works"*. The rest of the sentence is German: *"— the bound grips
now. Adjust the numbers and run the counter-probe."* The model then proceeded normally, in
German, for the remainder of the session.

**Note the shape:** the Hindi is not inserted *into* a German clause — it occupies the slot
where the German opening phrase would have been, and the German picks up cleanly after the
dash. Whatever selected the script did so for one phrase-sized unit at the very start of the
message.

---

## The window


[5405] TOOL_USE Bash:
grep -n "lokal" crates/gabbro-check/src/kosten.rs | head -8

[5406] TOOL_RESULT:
90:        let lokal = f
101:            lokal,
190:    lokal: HashMap<String, crate::typen::Typ>,
365:                let name = match self.u.typ_von_ort(self.modul, o, &self.lokal).durchgreifen() {
385:        let t = self.u.typ_von_ort(self.modul, o, &self.lokal);
403:        let t = self.u.typ_von_ort(self.modul, o, &self.lokal);
657:        let lokal = f
668:            lokal,

[5408] TOOL_USE Bash:
sed -n '86,102p' crates/gabbro-check/src/kosten.rs; sed -n '325,345p' crates/gabbro-check/src/kosten.rs

[5409] TOOL_RESULT:
        };
        let FnRumpf::Block(b) = &f.rumpf else {
            return;
        };
        let lokal = f
            .parameter
            .iter()
            .map(|p| (p.name.text.clone(), u.typ_von_ausdruck_decl(modul, &p.typ)))
            .collect();
        let r = Rechner {
            u: &u,
            modul,
            deklariert: &deklariert,
            haltezeiten: &haltezeiten,
            geteilte_haltezeiten: &geteilte_haltezeiten,
            lokal,
        };
    fn domaenenschranke(&self, d: &Domaene) -> Option<i128> {
        let tabelle = match d {
            // **`ancestors of` erbt die Schranke von `descendants of`** -- dieselbe Kante,
            // andere Ri

[5410] TOOL_USE Bash:
grep -n "fn typ_von_ausdruck_decl" -A6 crates/gabbro-check/src/umgebung.rs | head -8; grep -n "kapazitaeten" crates/gabbro-check/src/umgebung.rs | head -5

[5411] TOOL_RESULT:
543:    pub fn typ_von_ausdruck_decl(&self, von: &str, t: &TypExpr) -> Typ {
544-        let mut unterwegs = HashSet::new();
545-        self.typexpr(von, t, &mut unterwegs)
546-    }
547-
548-    fn typexpr(&self, von: &str, t: &TypExpr, unterwegs: &mut HashSet<String>) -> Typ {
549-        match t {
59:    pub kapazitaeten: HashMap<String, u128>,
262:                        self.kapazitaeten.insert(q(&t.name.text), n as u128);
615:                    .find_map(|k| self.kapazitaeten.get(&k).copied())
743:                        laenge: self.kapazitaeten.get(t).copied(),

[5412] TOOL_USE Bash:
sed -n '347,375p' crates/gabbro-check/src/kosten.rs | grep -n "kapaz\|tabelle" 

[5413] TOOL_RESULT:
(Bash completed with no output)

[5414] TOOL_USE Bash:
awk 'NR>=325 && NR<=400 && (/kapazit/ || /let tabelle/ || /^        };/ || /Some\(/)' crates/gabbro-check/src/kosten.rs | head -8

[5415] TOOL_RESULT:
        let tabelle = match d {
                    .find(|(k, _)| *k == &name || k.rsplit("::").next() == Some(kurz.as_str()))
        };
        self.u.kapazitaeten.get(&tabelle).map(|n| *n as i128)
        };

[5417] TOOL_USE Bash:
grep -n "fn kandidaten" crates/gabbro-check/src/umgebung.rs

[5418] TOOL_RESULT:
123:    pub fn kandidaten_oeffentlich(&self, von: &str, pfad: &str) -> Vec<String> {
127:    fn kandidaten(&self, von: &str, pfad: &str) -> Vec<String> {

[5419] TOOL_USE Bash:
python3 - <<'PYEOF'
p='crates/gabbro-check/src/kosten.rs'; s=open(p,encoding='utf-8').read()
a="        self.u.kapazitaeten.get(&tabelle).map(|n| *n as i128)"
b='''        // **Der Name kann unqualifiziert sein** -- `index into Topologie` nennt die Tabelle
        // ohne Modulpfad, waehrend `kapazitaeten` qualifiziert schluesselt. Ohne diesen
        // Umweg fiel die Schranke still aus, und `K003` machte daraus eine Absage ueber die
        // DEKLARATION statt ueber die Aufloesung.
        self.u
            .kapazitaeten
            .get(&tabelle)
            .copied()
            .or_else(|| {
                self.u
                    .kandidaten_oeffentlich(self.modul, &tabelle)
                    .into_iter()
                    .find_map(|k| self.u.kapazitaeten.get(&k).copied())
            })
            .map(|n| n as i128)'''
assert a in s; s=s.replace(a,b,1); open(p,'w',enco

[5420] TOOL_RESULT:
ok
Fehler: [K002] beispiele/18-vorfahren.gab:43:11: der Block haelt `TOPO` fuer 768 ops, die Sperre sagt `held <= 600 ops` zu
beispiele/18-vorfahren.gab: 5 Items, 1 Fehler, 0 Hinweise

[5422] assistant TEXT:
काम करता है — die Schranke greift jetzt. Zahlen anpassen und Gegenprobe.

[5423] TOOL_USE Bash:
sed -i 's/rank 1 held <= 600 ops/rank 1 held <= 800 ops/; s/costs   <= 800 ops/costs   <= 900 ops/' beispiele/18-vorfahren.gab && cargo run -q --bin gabbro -- pruefe beispiele/18-vorfahren.gab 2>&1|grep -E "Fehler:|Items"; cargo run -q --bin gabbro -- pruefe beispiele/*.gab 2>&1|grep -E "^beispiele/"|grep -v "0 Fehler"; echo "(leer=alle sauber)"; cargo test -q 2>&1|grep -cE FAILED

[5426] TOOL_RESULT:
beispiele/18-vorfahren.gab: 5 Items, 0 Fehler, 0 Hinweise
(leer=alle sauber)
0

[5429] TOOL_USE Write:
<repo>/beispiele/gift/69-vorfahren-ohne-schranke.gab

[5430] TOOL_RESULT:
File created successfully at: <repo>/beispiele/gift/69-vorfahren-ohne-schranke.gab (file state is current in your context — no need to Read it back)

[5431] TOOL_USE Bash:
cargo run -q --bin gabbro -- pruefe beispiele/gift/69-vorfahren-ohne-schranke.gab 2>&1|grep -E "Fehler:|Items"



---

## Two observations that may narrow it

1. **Position.** The anomaly is the first token of an assistant message that follows a
   successful tool result. Every other message in this window begins with German prose in the
   same position. If there is a pattern worth grepping for in other reports, it may be
   *message-initial short interjections after a success signal*.

2. **No Indic content anywhere.** The window contains German prose, Rust source, shell
   commands and compiler output. No Hindi, no transliteration, no Unicode above Latin-1 apart
   from German umlauts and typographic dashes. Nothing in the immediate context could have
   primed the script.

Steps to Reproduce

unknown

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.220 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

_No response_

View original on GitHub ↗