{{ message }}
Repository navigation
Give each chat stream failure a stable code so the interface words it in the reader's language - #951
Merged
Conversation
… in the reader's language Signed-off-by: dada-yan <BinjunYann@gmail.com>
WaylandYang
approved these changes
Sep 26, 2026
WaylandYang
left a comment
Contributor
There was a problem hiding this comment.
The stream's error frame now carries the same {error, code} envelope a refused request does, so the interface words it and MCP, logs and older clients keep the English sentence. Reading the model side's codes from the error types utopia_llm already distinguishes, and pinning the finalization reasons with a test that builds them from finalization_error itself, is the right way to keep the codes honest. Tightening the terminal contract to require a code on every failure row is the part that keeps this true. Merging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
ADR 0004 makes one hard rule: the server produces no display text. An error a user can hit carries a
code, and the interface words it from itserr.*table. The English message stays for MCP, CLI and logs. Refused requests follow this rule, and #931 gave the chat request the same wording.Failures inside the chat stream do not follow it. The
errorframe is a plain English sentence, and the web client shows it as it is. In the Chinese interface, these all appear in English:Change
api/chat.rs):A failed generation carries a
Failure { code, message }instead of a string.The
errorframe's data is the same envelope as a refused request:{"error": <message>, "code": <code>}. The English message is unchanged.The codes:
answer_emptyanswer_tool_textanswer_tool_callanswer_too_longanswer_failedanswer_not_savedsearch_failedstream_ended,stream_laggedmodel_out_of_credit,model_rate_limited,model_unavailable,model_unreachable,model_rejectedModel failures take their code from the types
utopia_llmalready has:OutOfCredit,RateLimited,Unavailable,UnreachableorInterrupted, andRejected. Anything else isanswer_failed.The finalization reasons from
agent::finalization_errorreachchat_finalizationas text, sounpublishable_codereads them by their wording. A unit test builds the reasons withfinalization_erroritself, so rewording one fails that test instead of silently losing its code.api.ts):errorframe is read with the samerefusalMessageas a refused request.err.*inen.tsandzh.ts.zhis typedtypeof S, so a missing entry fails the build.Relation to #946
#946 added one more failure exit in the same place: an ordinary turn that writes its tool call as text a second time. This branch is rebased onto dev after #946 and codes that exit
answer_tool_text, and the new tests check it.Verification
Run in containers on a Linux host (Rust 1.98.1, Postgres 16 with pgvector; Node 20 with pnpm 10.2.1).
Terminal contract (
chat_terminal_tests.rs):codeand message".New tests in
chat_error_code_tests.rsrun through the realchathandler against a scripted endpoint:answer_empty;answer_tool_text;answer_tool_text;model_out_of_credit,model_rate_limited,model_unavailableormodel_rejected;answer_not_saved;the_finalization_reasons_keep_their_codes(unit test).Stream test (
chat_stream_tests.rs): a producer that disappears ends instream_ended.Web (
chatStream.test.ts):err.*, both on a new request and on a reattachment;On dev (b07af8f), I added only the tests that compile there: the handler tests, the terminal contract and the web test. Each fails:
The unit test and the stream-test change use the new function and the new signature, so they were left out of that run.
With this change:
cargo fmt --all --checkpasses.cargo clippy --workspace --all-targets -- -D warningspasses.cargo test -p utopia-server api::: 174 passed.cargo test --workspacewithUTOPIA_TEST_REQUIRE_DB=1andUTOPIA_TEST_REQUIRE_PDFTOTEXT=1: 1189 passed, 0 failed, 5 ignored (the existing#[ignore]tests).cargo build --workspacepasses.pnpm install --frozen-lockfile,pnpm test(145 passed) andpnpm buildpass.Limits
answer_failedis the catch-all. Rarer failures share it, such as a final answer that timed out or a finish reason we do not know.