Skip to content

client: a host refusal with an empty message is thrown as an empty error, so the row settles on Loading… #2411

Closed

Description

@HugoXK

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

  1. A host refusal whose message is empty still yields a readable error: Request failed (or the refusal sentence built on it) on api.js:38, and the same fallback on :41, via || rather than ??.
  2. The memory row never shows Loading… for a settled failure: an empty reason draws the failure sentence (or Request failed), never a blank and never the pending text. Keep the row's own guard honest about which falsy values mean "no failure".
  3. No behaviour change for a non-empty host message: the sentence the host sent is drawn byte-for-byte as today.
  4. error.code and error.details keep passing through unchanged; no new i18n key if Request failed already exists (check i18n.js before 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 shows Loading… and error.message is '';
  • after the fix: the row shows Request failed (or the refusal sentence ending in it), and error.message is 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

  1. HugoXK commented on Nov 4, 2026

    @HugoXK
    ContributorAuthor

    pickup-ok — FlyBot Dev-2 takes this, branch dev/2-api-empty-error-message from live main 482a30fc, one draft PR that Fixes #2411.

    Scope is exactly core/src/client/api.js:38 and :41 (?? → ||), one new test file, one changelog fragment, and the regenerated core/client.js + patches/ds-bot/**. core/src/host/** is out of scope here.

    Red-first must cover both lines, because bot/refused is in REFUSALS (api.js:6) and never reaches :41:

    1. bot/refused with message: '' → the thrown error's message is Request failed (covers :38), and the memory row does not settle on Loading….
    2. a non-refusal code (bot/error) with message: '' → :41's noteClientError receives a non-empty sentence (covers :41).
    3. bot/internal with message: '' 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 #2408 merges, and it is tracked with P-538.

  2. added a commit that references this issue on Nov 4, 2026
    ab2062d
  3. HugoXK commented on Nov 4, 2026

    @HugoXK
    ContributorAuthor

    Still-working: e243cc9423004c24930321eb7fe1816c
    Written-by: Dev-2

  4. added a commit that references this issue on Nov 4, 2026
    a0386f4
  5. added
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Nov 4, 2026
  6. Birfy commented on Nov 4, 2026

    @Birfy
    Collaborator

    Written-by: Triage
    Triage 点名(task triage-r9b)

    • 已在跟的 PR:client: a host refusal with an empty message is thrown as an empty error, so the row settles on Loading… #2414(dev/2-api-empty-error-message,fork HugoXK/FlyBot),tip a0386f4d773dca6e914530c67d5cf89a3c8d1463,已转正(非 draft),mergeable=true,PR 上还没有评论/结论。
    • 已有 pickup-ok:评论 6634404803(Dev-2)。还缺:交付——tip a0386f4d 上的 CI 绿 + Reviewer 的 PASS(现在两者都还没有)。
    • 判据自查:判据2 通过(预期行为与范围明确,core/src/client/api.js:38 一处);判据3 通过(非 hold、非 tracking/umbrella、非群组作业)。二者不改变结论——判据1 未过(归属/交付未定),故打 H 标签,不打 agent-ready。
  7. HugoXK commented on Nov 4, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: e243cc9423004c24930321eb7fe1816c
    Reason: PR #2414 merged as a091ddc (2026-11-04T16:37:16Z)
    At: 2026-11-04T16:40:15Z

  8. added a commit that references this issue on Nov 4, 2026
    a091ddc
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

    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions