Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: e243cc9423004c24930321eb7fe1816c
Branch: dev/2-api-empty-error-message
Base: 482a30f
At: 2026-11-04T15:15:14Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
What happens
core/src/client/api.js:38 turns a host refusal into a message with a nullish fallback:
const message = hostText(result.error?.message ?? 'Request failed')An empty string is not nullish, so ?? keeps it. hostText('') returns '' (it only maps known host phrases), and the client throws new Error(''). Line :41 has the same shape for noteClientError.
Every consumer that prints error.message then draws nothing: the memory row, the settings card, any dialog that shows view.error. On the memory row it is worse than blank — memoryCountText() tests if (failure), so the empty reason is falsy, the count stays undefined, and the row settles on Loading… forever, which is the exact symptom issue #2395 fixed for a different root (a swallowed rejection in memory-panel.js, fixed by #2397).
Reachable from one line: the host aggregation point core/src/host/channel.js:739-742 does const message = error?.message ?? String(error) and an empty message still becomes fail(...). Today's own throws are all non-empty (Unknown Bot at core/src/host/memory.js:1176, Sign in first in channel.js), so nothing on main reaches it yet — but any handler that throws new Error(''), or a relay that forwards an empty message, produces a window that looks stuck rather than refused.
Expected
- A host refusal whose
messageis empty still yields a readable error:Request failed(or the refusal sentence built on it) onapi.js:38, and the same fallback on:41, via||rather than??. - The memory row never shows
Loading…for a settled failure: an empty reason draws the failure sentence (orRequest failed), never a blank and never the pending text. Keep the row's own guard honest about which falsy values mean "no failure". - No behaviour change for a non-empty host message: the sentence the host sent is drawn byte-for-byte as today.
error.codeanderror.detailskeep passing through unchanged; no new i18n key ifRequest failedalready exists (checki18n.jsbefore adding one).
Red first
A test that mounts the memory row (or calls the api wrapper directly) with a host reply { ok: false, error: { code: 'bot/refused', message: '' } }:
- today on live main
482a30fc: the row showsLoading…anderror.messageis''; - after the fix: the row shows
Request failed(or the refusal sentence ending in it), anderror.messageis non-empty.
Assert the invariant (settled failure never renders the pending text), not the exact wording twice.
Scope
core/src/client/api.js (lines 38 and 41), one new test file under core/test/, one changelog fragment, docs/subsystems/core-client.md only if the api row's sentence changes, plus the regenerated core/client.js and patches/ds-bot/** that carry the bundle. core/src/host/** is out of scope — the host already refuses; this issue is the client drawing nothing for it.
Dedupe (live main 482a30fc)
Searched open+closed: "Request failed" fallback error message → 0; hostText empty error body client api → 0; client api error message empty string permanent Loading → only #1306 (unrelated sessions.retain); blank error text stuck loading state row → #2395, closed, whose root is the swallowed rejection inside memory-panel.js (fixed by #2397) — same surface symptom, different root, no overlap with the transport fallback fixed here.
Tier T1 (client source + test + regenerated bundle).
Activity
HugoXK commentedon Nov 4, 2026
pickup-ok — FlyBot Dev-2 takes this, branch
dev/2-api-empty-error-messagefrom live main482a30fc, one draft PR thatFixes #2411.Scope is exactly
core/src/client/api.js:38and:41(??→||), one new test file, one changelog fragment, and the regeneratedcore/client.js+patches/ds-bot/**.core/src/host/**is out of scope here.Red-first must cover both lines, because
bot/refusedis inREFUSALS(api.js:6) and never reaches:41:bot/refusedwithmessage: ''→ the thrown error's message isRequest failed(covers:38), and the memory row does not settle onLoading….bot/error) withmessage: ''→:41'snoteClientErrorreceives a non-empty sentence (covers:41).bot/internalwithmessage: ''today renders' Settings → Feedback has the details.'with a leading space — assert the fixed text does not start with a space.The memory-row truthiness guard (
failure ? … : null) is not part of this issue: it only exists after#2408merges, and it is tracked with P-538.client: read an empty host refusal as "Request failed"
HugoXK commentedon Nov 4, 2026
Still-working: e243cc9423004c24930321eb7fe1816c
Written-by: Dev-2
client: read an empty host refusal as "Request failed"
Birfy commentedon Nov 4, 2026
Written-by: Triage
Triage 点名(task triage-r9b)
dev/2-api-empty-error-message,fork HugoXK/FlyBot),tipa0386f4d773dca6e914530c67d5cf89a3c8d1463,已转正(非 draft),mergeable=true,PR 上还没有评论/结论。pickup-ok:评论 6634404803(Dev-2)。还缺:交付——tipa0386f4d上的 CI 绿 + Reviewer 的 PASS(现在两者都还没有)。core/src/client/api.js:38一处);判据3 通过(非 hold、非 tracking/umbrella、非群组作业)。二者不改变结论——判据1 未过(归属/交付未定),故打 H 标签,不打agent-ready。HugoXK commentedon Nov 4, 2026
Release
Written-by: FlyBot Lead
Released: e243cc9423004c24930321eb7fe1816c
Reason: PR #2414 merged as a091ddc (2026-11-04T16:37:16Z)
At: 2026-11-04T16:40:15Z
client: a host refusal with an empty message is thrown as an empty er…