fix(trpc): retry chat session insert without v2WorkspaceId on FK violation

AN
2mo ago
+274-18

Fixes #6230

Bug

Sending a message in a chat pane for a "local" entry (the type: "main" workspace, i.e. the repo checkout itself) fails with a database error, in every repo. Worktree workspaces work — only "local" entries fail.

Root cause

chat.createSession (packages/trpc/src/router/chat/chat.ts) inserted chat_sessions.v2_workspace_id directly from the client-supplied input.v2WorkspaceId. That column has an FK to cloud v2_workspaces (chat_sessions_v2_workspace_id_v2_workspaces_id_fk).

Since local-first (#5731), workspaces are host-owned: the main workspace is minted locally with a fresh UUID on every boot (runMainWorkspaceSweep → ensureMainWorkspace) and is never mirrored to the cloud v2_workspaces table. The FK can therefore never be satisfied for those ids, and the insert throws on every message — surfacing the raw query in the chat pane.

The legacy REST bootstrap (apps/api/src/app/api/chat/[sessionId]/route.ts) already carries a defensive retry-without-workspaceId fallback for this exact failure class; the tRPC path had none.

Fix

Extract the session insert into create-chat-session.ts and retry without v2WorkspaceId when the insert fails with a foreign-key error — mirroring the REST route's shouldRetryWithoutWorkspaceId behavior. The retry runs in a fresh transaction because a failed INSERT aborts the surrounding Postgres transaction (current transaction is aborted), so re-inserting inside the same one would fail regardless.

The resulting row keeps v2WorkspaceId NULL for local-first workspaces — the same degraded-but-functional state the REST path produces.

Tests

Added packages/trpc/src/router/chat/utils/create-chat-session.test.ts (6 cases):

  • insert with v2WorkspaceId succeeds and returns the txid
  • FK violation → retries without v2WorkspaceId
  • generic foreign-key error → retries
  • non-FK errors are propagated
  • onConflictDoNothing conflict → { txid: null }
  • FK violation logs exactly once

bun test: 6 pass, 0 fail. Package typecheck: clean. Biome check: clean.

Note: #5853 (bot draft, #5852) targets the same FK with an up-front existence check; this PR takes the retry approach already established in the REST route.


Summary by cubic

Retries chat session insert without v2WorkspaceId only when the v2_workspace_id FK rejects it, so messages from the local “main” workspace send. Previously packages/trpc wrote the client’s v2WorkspaceId and surfaced FK errors; now it retries in a fresh transaction and stores a null v2WorkspaceId for local sessions, matching the REST route.

  • Extracts createChatSession in packages/trpc/src/router/chat/utils/create-chat-session.ts and uses it from packages/trpc/src/router/chat/chat.ts.
  • Retries only when the error matches chat_sessions_v2_workspace_id_v2_workspaces_id_fk or a v2_workspace_id FK violation; does not retry on unrelated FK or column errors.
  • Uses a new transaction for the retry; on conflict, returns { txid: null }.
  • Hardens error handling: safe message parsing avoids calling toLowerCase on undefined; retry log emits a fixed reason without raw ids or the DB error.
  • Tests cover success, targeted FK retry, no-retry cases, fresh-transaction fallback, conflict handling, and a single sanitized warning.

Summary by CodeRabbit

  • Bug Fixes

    • Improved chat-session creation reliability when legacy workspace references are unavailable.
    • Added targeted retry handling for recognized workspace-reference constraint errors.
    • Improved handling of session conflicts, transaction identifiers, and unrelated database failures.
    • Ensured retries use a fresh transaction and produce clear warning logs.
  • Tests

    • Added coverage for successful creation, retry behavior, conflicts, transaction isolation, warning logs, and error propagation.