{"record":{"id":"ca241700064ac86b","repo":"coleam00/Archon","slug":"cannot-signal-a-non-event-workflow-wait","errorCode":null,"errorMessage":"Cannot signal a non-event workflow wait","messagePattern":"Cannot signal a non-event workflow wait","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/db/workflows.ts","lineNumber":1629,"sourceCode":"        cursor.kind === 'quota' ? cursor.attempt : null,\n      ]\n    );\n  } catch (error) {\n    const err = error as Error;\n    getLog().error({ err, workflowRunId: id }, 'db.workflow_continuation_defer_failed');\n    throw new Error(`Failed to defer workflow continuation: ${err.message}`);\n  }\n}\n\n/** Atomically record the signal for one exact paused event wait. */\nexport async function signalWorkflowWait(\n  id: string,\n  waitContext: Extract<WorkflowWaitContext, { kind: 'event' }>,\n  payload?: unknown\n): Promise<{ signaled: boolean }> {\n  const parsedWaitContext = workflowWaitContextSchema.parse(waitContext);\n  if (parsedWaitContext.kind !== 'event') {\n    throw new Error('Cannot signal a non-event workflow wait');\n  }\n  const eventExpr =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->'wait'->>'event'\"\n      : \"json_extract(metadata, '$.wait.event')\";\n  const nodeExpr =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->'wait'->>'nodeId'\"\n      : \"json_extract(metadata, '$.wait.nodeId')\";\n  const signaledExpr =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->'wait'->>'signaledAt'\"\n      : \"json_extract(metadata, '$.wait.signaledAt')\";\n  const resumeAtExpr =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->'wait'->>'resumeAt'\"\n      : \"json_extract(metadata, '$.wait.resumeAt')\";\n  const signaledAt = new Date().toISOString();","sourceCodeStart":1611,"sourceCodeEnd":1647,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/db/workflows.ts#L1611-L1647","documentation":"A guard error thrown at the top of signalWorkflowWait: after Zod-parsing the waitContext, the code asserts the discriminated `kind` is 'event'. The public signature already types the parameter as the event-only Extract of WorkflowWaitContext, so hitting this at runtime means a non-event wait context (e.g. kind 'time'/'deadline') was passed despite the types, typically via untyped data or an any-cast.","triggerScenarios":"Calling signalWorkflowWait(id, waitContext) with a waitContext whose kind is not 'event' — passing a time/deadline wait context, or data loaded from JSON/storage that lost its literal type.","commonSituations":"Wiring a resume handler to the wrong wait context variable; deserializing wait context from a webhook payload or DB metadata without narrowing kind; using `any` to bypass the Extract<> parameter type.","solutions":["Narrow the wait context to kind === 'event' before calling signalWorkflowWait","Use the correct signaling API for time/deadline waits (they resume via listDueWorkflowContinuations, not signals)","Validate the waitContext with workflowWaitContextSchema and check parsed.kind === 'event' yourself first"],"exampleFix":"// before\nawait signalWorkflowWait(runId, waitCtx); // waitCtx.kind === 'time' -> throws\n// after\nif (waitCtx.kind !== 'event') {\n  throw new Error(`Cannot signal wait of kind ${waitCtx.kind}; only event waits accept signals`);\n}\nawait signalWorkflowWait(runId, waitCtx);","handlingStrategy":"validation","validationCode":"import { workflowWaitContextSchema } from '@archon/core';\nconst parsed = workflowWaitContextSchema.safeParse(waitContext);\nif (!parsed.success || parsed.data.kind !== 'event') {\n  throw new Error(`signalWorkflowWait requires an event wait, got kind=${(waitContext as any)?.kind}`);\n}","typeGuard":"function isEventWaitContext(w: WorkflowWaitContext): w is Extract<WorkflowWaitContext, { kind: 'event' }> {\n  return w.kind === 'event';\n}","tryCatchPattern":"if (!isEventWaitContext(waitContext)) {\n  log.error({ kind: (waitContext as { kind?: string }).kind }, 'refusing to signal non-event wait');\n  return { signaled: false };\n}\n// else proceed; wrap DB errors separately","preventionTips":["Always narrow on the discriminated `kind` before signaling","Avoid `any`-typed wait contexts crossing API boundaries","Load wait contexts through the schema parser, not raw JSON","Use time/deadline waits via the continuation path, not signals"],"tags":["workflow","validation","type-safety"],"backgroundTag":"invalid-argument-value","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}