Answering a decision from the composer throws unhandled on a dead link, instead of failing like every other send

Open Beginner friendly
#96 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
88/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
frontend

Research direction

Start in src/client/components/App.tsx:266-286 and compare the composer decision-answer path with the existing guarded send paths at lines 317-329 and in DecisionSheet.tsx:201-217. Run pnpm test -- composer, then add or update a regression test using answerFails: "reject" for the composer path. Done means the rejection shows the normal error, resets sending state, and produces no unhandled rejection.

Written by the indexing model from the issue text.

Description

What's wrong

App.tsx's submit() has two send paths. The plain-message path (src/client/components/App.tsx:317-329, the giveUp flow) wraps its postJson in a try/catch so a rejected fetch (a dead link, not just a bad status) fails visibly - text restored, an error shown. DecisionSheet.tsx's own send() (src/client/components/DecisionSheet.tsx:201-217) does the same with its own try/catch.

The composer's other send path - answering a decision without opening the sheet, App.tsx:266-286 - has no try/catch around its postJson call:

if (decision && !intake) {
  if (!choice && said === "" && attachments.length === 0) return;
  setSendState("sending");
  tap();
  const res = await postJson(`/api/sessions/${row.id}/answer`, { optionId: choice, text: said, images: attachments });
  if (!res.ok) { ... }
  ...
}

If postJson rejects (network down, dead link - fetches.tsx's test helpers call this "reject", as opposed to a bad HTTP status) rather than resolving with a bad status, this throws inside an async function called as void submit() from onSubmit (App.tsx:522), which becomes an unhandled promise rejection. The send state is left stuck on "sending" forever, no error is shown, and nothing is restored - the exact bug #60 and #86 both fixed for the other two send paths.

Found while writing tests/composer-haptics.test.tsx for #95: a test using answerFails: "reject" against this path produced Vitest caught 1 unhandled error during the test run instead of exercising the failure branch. The test was changed to use a bad status (answerFails: 502) instead, which does exercise !res.ok correctly - but the reject path itself is still unhandled in the real app.

Acceptance criteria

  • Answering a decision from the composer (not the sheet) with a rejected fetch fails the same visible way a bad status does: setError, failSend(), no unhandled rejection.
  • sendState does not get stuck on "sending" when this happens.
  • A regression test using answerFails: "reject" against this exact path (composer, not sheet) passes without an unhandled rejection.

Out of scope

  • DecisionSheet.tsx's own send() - already has a try/catch, not touched by this.
  • The plain-message path - already has a try/catch (giveUp), not touched by this.

Verification

pnpm test -- composer

Plus a new test exercising answerFails: "reject" against the composer's decision-answer path specifically.

Related

Found during #95. Same shape of bug as #60 (the sheet) and #86 (the plain-message path), both already fixed - this is the one path that was missed.

Dominant language
TypeScript
Stars
2
Forks
0
Avg merge
1h 12m
Merged PRs (30d)
46

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from jobizzness/bench

All issues in jobizzness/bench

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.