can1357/oh-my-pi · error
GPT-5 Harmony leak persisted after ${harmonyRetryAttempt} re
Error message
GPT-5 Harmony leak persisted after ${harmonyRetryAttempt} retries (${signalListLabel(err.detection.signals)}). What it means
The agent loop detects GPT-5 'Harmony' template leaks (raw harmony channel tokens like <|channel|> appearing in model output). It first tries abort-and-retry (up to 2 times) and truncate-and-resume recovery; if the leak still persists after the retry budget is exhausted, the loop throws this error instead of letting corrupted assistant output through. It is a deliberate escalation so callers fail loudly rather than consume leaked internal-format text.
Source
Thrown at packages/agent/src/agent-loop.ts:1289
if (err.recovered) {
if (harmonyTruncateResumeCount >= 2) {
await emitHarmonyAudit(config, err, "escalated", harmonyRetryAttempt);
throw new Error(
`GPT-5 Harmony leak recurred after truncate-and-resume recovery (${signalListLabel(err.detection.signals)}).`,
);
}
harmonyTruncateResumeCount++;
recovered = err.recovered;
message = recovered.message;
await emitHarmonyAudit(config, err, "truncate_resume", harmonyRetryAttempt);
// A recovered message completes the turn, so the abort-retry counter
// resets like the normal success path (the truncate-resume counter
// keeps accumulating for its cross-turn cap).
harmonyRetryAttempt = 0;
} else {
if (harmonyRetryAttempt >= 2) {
await emitHarmonyAudit(config, err, "escalated", harmonyRetryAttempt);
throw new Error(
`GPT-5 Harmony leak persisted after ${harmonyRetryAttempt} retries (${signalListLabel(err.detection.signals)}).`,
);
}
await emitHarmonyAudit(config, err, "abort_retry", harmonyRetryAttempt);
harmonyRetryAttempt++;
continue;
}
}
if (recovered) {
message = snapshotAssistantMessage(message);
currentContext.messages.push(message);
stream.push({ type: "message_start", message: snapshotAssistantMessage(message) });
stream.push({ type: "message_end", message: snapshotAssistantMessage(message) });
}
newMessages.push(message);
// The escalation choice (if any) applied to the call above; clear it so
// only the single escalation turn carries the forced choice.View on GitHub (pinned to 9690622007)
Solutions
- Retry the request later or switch to a different model/endpoint — repeated leaks usually indicate a provider-side regression, not a bug in your code
- Verify the model is being served through the correct harmony-dialect route (check PI_DIALECT / provider routing config); a non-harmony endpoint serving a harmony model leaks tokens
- Clear or shorten the conversation (compact/truncate history) — long or adversarial contexts increase leak probability
- Check provider status/changelog for a harmony template change and pin a known-good model revision
Example fix
// before: same corrupted session retried manually, still fails
await agent.prompt("continue"); // throws: Harmony leak persisted after 2 retries
// after: switch model or start a fresh session
agent.setModel("claude-opus-4"); // or agent.newSession() to drop the poisoned context
await agent.prompt("continue"); Defensive patterns
Strategy: retry
Validate before calling
// Check model routing before starting a harmony-sensitive session
const model = agent.getModel();
if (model && /gpt-5/i.test(model.id) && Bun.env.PI_DIALECT !== "harmony") {
console.warn("gpt-5 model without harmony dialect routing: leak risk");
} Type guard
function isHarmonyLeakError(err: unknown): err is Error & { message: string } {
return err instanceof Error && err.message.includes("Harmony leak persisted");
} Try / catch
try {
await agent.prompt(userInput);
} catch (err) {
if (isHarmonyLeakError(err)) {
// provider-side serialization problem: do NOT retry in a tight loop
logger.error("harmony leak persisted", { signals: err.message });
await compactOrRestartSession(agent); // drop poisoned context / switch model
} else throw err;
} Prevention
- Route harmony-family models through dialect-correct endpoints (check PI_DIALECT and provider config)
- Pin known-good model revisions and watch provider status pages for template regressions
- Compact long sessions proactively — leak probability grows with context size
- Audit harmony audit events (emitHarmonyAudit output) to catch truncate_resume warnings before they escalate
When it happens
Trigger: A streaming model response raises HarmonyLeakInterruption with recovered=false, and harmonyRetryAttempt is already >= 2 — i.e. three consecutive model responses from a GPT-5 harmony-dialect model leaked harmony control tokens and neither in-stream truncation nor re-issuing the request cleaned them up.
Common situations: Running GPT-5/gpt-5-codex-class models via a provider or proxy that mangles the harmony chat template (wrong dialect routing, custom base URL, patched sampler); very long contexts or unusual system prompts that push the model into emitting raw channel markers; provider-side incidents where the harmony serialization changes.
Related errors
- GPT-5 Harmony leak recurred after truncate-and-resume recove
- Soft tool requirement '${softRequiredTool}' was not satisfie
- Tool "${toolCall.name}" not found
- Validation failed for tool "${toolCall.name}": Tool call arg
- Validation failed for tool "${toolCall.name}":\n${errors}\n\
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b69a1c7f790d963d.
Report an issue: GitHub.