fix(dispatch): report a taken app id instead of a 500 - #5629
Conversation
startWorkspaceAppCreation checked for an app id collision in two places —
the cross-member pending guard and the reservation itself — and neither
call was inside a try. Both threw, so the message each one built, naming
the conflict and the owner holding it, was replaced by a generic
"Internal server error" in the create-app UI and in chat.
Both paths now throw WorkspaceAppIdTakenError, and the caller converts
only that error into a typed `app-id-taken` result carrying `conflict`
("registered" or "pending") and `owner`. Registry and storage failures
from the same helpers keep propagating, so an unreadable registry is
still a fault rather than a name the caller is told to change.
workspace-template-card treated every unrecognized mode as success and
would have toasted a green "remixed" for this one; it now reports it as
an error.
|
Here's a visual recap of what changed: Open the full interactive recap |
There was a problem hiding this comment.
Builder reviewed your changes — looks good ✅
Review Details
Code Review Summary
PR #5629 changes workspace app creation so registered and pending app-ID collisions become a typed app-id-taken result instead of an unhandled exception that is flattened to a generic 500. The implementation introduces WorkspaceAppIdTakenError, converts only that error at both collision gates, preserves registry/storage failures as real faults, extends the result union and agent instructions, and makes the template-card path show an error toast rather than falling through to success. The approach is sound and keeps the collision-specific recovery information (appId, conflict type, and pending owner) available to callers. Tests cover cross-member and same-owner pending collisions, and both independent code-review passes found no actionable issues. Risk assessment: Standard, because this changes shared dispatch business logic and several UI result consumers, but does not alter authentication, payments, schema, or destructive mutation behavior.
Findings
No confirmed actionable findings.
✅ Good coverage of the two previously unhandled collision paths.
✅ Catch blocks are deliberately narrow, so unreadable registry/storage state is not misreported as a user-fixable name conflict.
✅ The template-card unknown-result success fallthrough is corrected for the new mode.
🧪 Browser testing: Attempted after review; 0/11 cases could be verified because browser executor sessions did not expose Chrome navigation/snapshot/click/screenshot tools. The dev server was healthy, and this is an infrastructure limitation rather than an application failure.

What happened
Creating a workspace app whose id is already taken returned
{"error":"Internal server error"}in the create-app popover, with no indication of the real cause.startWorkspaceAppCreationchecks for an id collision in two places, and neither call was inside atry:assertPendingWorkspaceAppCreationAvailable— a pending creation owned by another memberreservePendingWorkspaceApp— a registered app, or a pending creation owned by anyone, including the callerEach builds a message that names the conflict and, for an in-flight creation, who holds it. Both threw, so the action route flattened them to a generic 500 and that message never reached the UI or chat.
The second gate is what users actually hit: start an app, have it stall in
pending, retry the same prompt, get "Internal server error" with nothing to act on.Changes
WorkspaceAppIdTakenErrorcarriesappId,conflict("registered" | "pending") andowner. Both collision paths throw it.startWorkspaceAppCreationconverts only that error into a newapp-id-takenresult mode. Registry and storage failures from the same helpers keep propagating — an unreadable app registry stays a fault, rather than being reported as a name the caller can simply change.workspace-template-cardfell through totoast.success(labels.remixSuccess)for any mode it did not recognize, so this result would have rendered a green "remixed" toast on a failure. It now reports it as an error.simple-agents-panel's result union and thestart-workspace-app-creationagent instructions inintegrations.tsboth learn the new mode.The create-app popover and
NewWorkspaceAppFlowneeded no change: their fallback branch already rendersresult.message, which is exactly what was being thrown away.This follows the repo's own rule that a failure coerced into a value callers cannot distinguish from success is a bug — the template card's silent-success default was that shape.
Testing
app-creation-store.spec.ts: 63 passed. The two cross-member collision tests now assert the typed result instead of a throw, plus a new test covering the same-owner in-flight case that produced the original 500.packages/dispatchsuite: 737 passed, 0 failed.oxfmtrun on all modified files; changeset added.Four spec files in the full run fail to load with
Failed to resolve import "qrcode.react", and 13tscerrors remain — all from stale dependencies in my local checkout (@agent-native/corewill not build: missing@better-auth/scim). None are in the files this PR touches, and none are test failures.