{"record":{"id":"37c775407e76558a","repo":"coleam00/Archon","slug":"failed-to-signal-workflow-wait-err-message","errorCode":null,"errorMessage":"Failed to signal workflow wait: ${err.message}","messagePattern":"Failed to signal workflow wait: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/db/workflows.ts","lineNumber":1701,"sourceCode":"        await insertWorkflowEvent(query, {\n          workflow_run_id: id,\n          event_type: 'wait_signaled',\n          step_name: workflowWaitStepName(parsedWaitContext),\n          data: {\n            event: parsedWaitContext.event,\n            ...(payload !== undefined ? { payload } : {}),\n          },\n        });\n      }\n      return { signaled };\n    });\n  } catch (error) {\n    const err = error as Error;\n    getLog().error(\n      { err, workflowRunId: id, event: parsedWaitContext.event },\n      'db.workflow_wait_signal_failed'\n    );\n    throw new Error(`Failed to signal workflow wait: ${err.message}`);\n  }\n}\n\n/**\n * Atomically CLAIM the container write-back apply before the live root is mutated\n * (R2-F4). A conditional UPDATE that sets `metadata.writeback_apply_claimed = true`\n * only while it is unset — so exactly one resume wins the claim. Returns whether\n * THIS caller won. The caller must apply the overlay only on `claimed === true`, and\n * on apply FAILURE release the claim (`releaseWritebackClaim`) so a `workflow resume`\n * can retry; on a crash AFTER a successful apply the claim stays set, so the next\n * resume finds it claimed and does NOT re-apply (no path applies twice).\n */\nexport async function claimWriteback(id: string): Promise<{ claimed: boolean }> {\n  const dialect = getDialect();\n  const extract =\n    getDatabaseType() === 'postgresql'\n      ? \"metadata->>'writeback_apply_claimed'\"\n      : \"json_extract(metadata, '$.writeback_apply_claimed')\";","sourceCodeStart":1683,"sourceCodeEnd":1719,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/db/workflows.ts#L1683-L1719","documentation":"Thrown by signalWorkflowWait when the database work behind recording an event-wait signal fails. After the event/non-event guard passes, the function runs a conditional UPDATE to atomically record the signal payload in metadata.wait; any driver/SQL failure is logged as `db.workflow_wait_signal_failed` (with runId and event) and rethrown with this message. `{signaled:false}` is returned when the paused row does not match — only an actual DB failure throws.","triggerScenarios":"Calling signalWorkflowWait for a paused event wait when the connection fails, the metadata UPDATE errors (JSON path invalid for dialect, lock contention, schema mismatch), or payload serialization fails at the driver.","commonSituations":"Signaling from a webhook handler while the DB is briefly unavailable; corrupted metadata JSON on the run row; mismatched database type configuration breaking the JSON extraction expressions.","solutions":["Check the `db.workflow_wait_signal_failed` log entry for the underlying driver error","Verify DB connectivity and retry the signal — recording is atomic and safe to re-issue","Confirm metadata.wait JSON on the run row is valid and the configured dialect matches the schema","If signaled:false repeatedly, confirm the run is still status='paused' with the exact wait.event/nodeId"],"exampleFix":"// before\nawait signalWorkflowWait(runId, waitCtx, payload); // throws on DB failure\n// after\nconst { signaled } = await signalWorkflowWait(runId, waitCtx, payload);\nif (!signaled) throw new Error(`run ${runId} has no matching paused event wait`);","handlingStrategy":"try-catch","validationCode":"// confirm there is a matching paused event wait first\nconst run = await getWorkflowRun(id);\nconst wait = run?.metadata?.wait;\nconst signalable = run?.status === 'paused' && wait?.kind === 'event' && wait.event === waitContext.event;","typeGuard":"function isSignalableEventRun(run: WorkflowRun | undefined, ctx: Extract<WorkflowWaitContext, { kind: 'event' }>): boolean {\n  const w = run?.metadata?.wait;\n  return run?.status === 'paused' && w?.kind === 'event' && w.event === ctx.event && w.nodeId === ctx.nodeId;\n}","tryCatchPattern":"try {\n  const { signaled } = await signalWorkflowWait(id, waitContext, payload);\n  if (!signaled) log.warn({ id }, 'no matching paused event wait for signal');\n} catch (error) {\n  log.error({ id, err: error }, 'signal failed at DB layer');\n  throw error;\n}","preventionTips":["Retry signals idempotently — recording is atomic and safe to re-issue","Keep the run paused until the signal is confirmed","Preserve metadata.wait structure; do not hand-edit run JSON","Monitor `db.workflow_wait_signal_failed` for webhook-signaling reliability"],"tags":["database","workflow","signal"],"backgroundTag":"database-query-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}