Bug
POST /session/{id}/prompt_async returns 400 because the backend validates that part IDs must start with "prt", but generatePartId() in packages/core/src/api/prompt.ts produces IDs like part-1742244612345-0.
Root Cause
Two issues:
1. Part ID prefix mismatch
// packages/core/src/api/prompt.ts:25-27
let partIdCounter = 0
function generatePartId(): string {
return `part-${Date.now()}-${partIdCounter++}` // ❌ backend expects "prt" prefix
}
The backend validates part IDs with a regex requiring the prt prefix. The convertToApiParts function in utils/prompt-api.ts also generates IDs using crypto.randomUUID() which don't match either.
2. SDK errors silently swallowed
The generated @hey-api/openapi-ts SDK returns { error: { status, statusText, body } } objects for non-2xx responses instead of throwing. Effect.tryPromise in packages/core/src/atoms/sessions.ts only catches thrown errors, so HTTP 400 responses resolve as success — the UI shows "prompt sent successfully" but the backend rejected it.
// packages/core/src/atoms/sessions.ts:162-174
yield* Effect.tryPromise({
try: async (_signal) => {
const result = await client.session.promptAsync({...})
// result.error is never checked — 400 looks like success
return result
},
catch: (error) => new Error(`Failed to send prompt: ${...}`),
})
Impact
- All prompt sends silently fail with 400
- User sees no error toast ("prompt sent successfully" log)
- New messages never appear in the chat
Fix
- Change prefix from
part- to prt- in generatePartId()
- Check
result.error after SDK call and throw if present
Logs
POST /api/opencode/4056/session/ses_xxx/prompt_async 400
Backend error: Expected a string starting with "prt", got "test-1" at ["parts"][0]["id"]
Bug
POST /session/{id}/prompt_asyncreturns 400 because the backend validates that part IDs must start with"prt", butgeneratePartId()inpackages/core/src/api/prompt.tsproduces IDs likepart-1742244612345-0.Root Cause
Two issues:
1. Part ID prefix mismatch
The backend validates part IDs with a regex requiring the
prtprefix. TheconvertToApiPartsfunction inutils/prompt-api.tsalso generates IDs usingcrypto.randomUUID()which don't match either.2. SDK errors silently swallowed
The generated
@hey-api/openapi-tsSDK returns{ error: { status, statusText, body } }objects for non-2xx responses instead of throwing.Effect.tryPromiseinpackages/core/src/atoms/sessions.tsonly catches thrown errors, so HTTP 400 responses resolve as success — the UI shows "prompt sent successfully" but the backend rejected it.Impact
Fix
part-toprt-ingeneratePartId()result.errorafter SDK call and throw if presentLogs