Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Inbox cancel returns 204 even when nothing was withdrawn

Open Beginner friendly
#56,735 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
typescript
Domain
api, backend

Research direction

Inspect packages/server/src/handlers/session.ts, especially session.inbox.cancel and the sibling session.inbox.update, then locate the corresponding session inbox handler tests. Reproduce cancellation after an item has been delivered and verify that a real withdrawal remains distinguishable from a conflict. The work is done when the response behavior matches the selected API contract and the relevant tests cover both cases.

Written by the indexing model from the issue text.

Description

Description

What happens. In v2.0.32, DELETE /api/session/:sessionID/inbox/:inboxID answers 204 No Content in two different situations:

  • the pending item was withdrawn, so it will never reach the model;
  • the item was no longer pending, for example because it had already been delivered to the session.

The handler catches the conflict and returns the same empty success (packages/server/src/handlers/session.ts, lines 551–559 at v2.0.32):

.handle(
  "session.inbox.cancel",
  Effect.fn(function* (ctx) {
    yield* session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }).pipe(
      Effect.catchTag("Session.NotFoundError", missingSession),
      Effect.catchTag("Session.InboxConflictError", () => Effect.void),
    )
    return HttpApiSchema.NoContent.make()
  }),
)

The sibling session.inbox.update handler (line 564) sends the same InboxConflictError through pendingMutation, which answers 409 Conflict. Only cancel hides it.

Why it matters. A client that offers "undo my queued message" wants to put the withdrawn text back in the input box. Between the client's inbox read and the cancel, the running turn can end and the item can be delivered. The cancel still returns 204. If the client then restores the text, the person may send the same request again, although the first copy already ran. The client cannot tell the two cases apart:

  • the item is absent from a fresh inbox read in both cases;
  • the transcript proves delivery only when the message is in the page the client reads.

Request. Let the cancel response say what happened. For example:

  • answer 409 Conflict for an item that is no longer pending, as session.inbox.update already does, and keep 204 for a real withdrawal; or
  • if changing the status would break existing callers, return a small body such as { "withdrawn": true } or { "withdrawn": false }.

Either form lets a client restore the text only after a real withdrawal, and otherwise point the person to the delivered message.

Plugins

None

OpenCode version

2.0.32

Steps to reproduce
  1. Start a reply that takes a few seconds.
  2. While it runs, queue a follow-up message. It appears in GET /api/session/:sessionID/inbox.
  3. Wait until the reply ends and the follow-up appears in GET /api/session/:sessionID/message.
  4. Send DELETE /api/session/:sessionID/inbox/:inboxID for that follow-up. The answer is 204, the same as for a real withdrawal.
Dominant language
TypeScript
Stars
215k
Forks
28.9k
Avg merge
8h 11m
Merged PRs (30d)
365

Getting set up

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 anomalyco/opencode

All issues in anomalyco/opencode

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.