jobizzness / jobizzness/bench

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
Dominant language
TypeScript
Stars
1
Forks
0
Avg merge
1h 21m
Merged PRs (30d)
41

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:

```ts
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.

Contributor guide

Open the contributing guide

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.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.