{"record":{"id":"2edba46cc1f0cb68","repo":"koala73/worldmonitor","slug":"finalizewaverun-run-runid-is-run-status","errorCode":null,"errorMessage":"[_finalizeWaveRun] run ${runId} is ${run.status}, expected broadcast-created","messagePattern":"\\[_finalizeWaveRun\\] run (.+?) is (.+?), expected broadcast-created","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/waveRuns.ts","lineNumber":1420,"sourceCode":"    sentAt: v.number(),\n  },\n  handler: async (ctx, { runId, sentAt }) => {\n    const run = await ctx.db\n      .query(\"waveRuns\")\n      .withIndex(\"by_runId\", (q) => q.eq(\"runId\", runId))\n      .unique();\n    if (!run) throw new Error(`[_finalizeWaveRun] no run ${runId}`);\n\n    // Status check FIRST — broadcastId presence is downstream of being in\n    // broadcast-created status. Checking presence before status would mask\n    // a wrong-status caller behind a misleading \"missing broadcastId\" error.\n    if (run.status === \"sent\") {\n      // Idempotent: a duplicate finalize on an already-sent run is a no-op,\n      // not an error. Don't re-advance the tier.\n      return { ok: true as const, alreadySent: true as const, advancedToTier: undefined };\n    }\n    if (run.status !== \"broadcast-created\") {\n      throw new Error(\n        `[_finalizeWaveRun] run ${runId} is ${run.status}, expected broadcast-created`,\n      );\n    }\n    if (!run.broadcastId || !run.segmentId) {\n      throw new Error(\n        `[_finalizeWaveRun] run ${runId} missing broadcastId/segmentId`,\n      );\n    }\n\n    const config = await ctx.db\n      .query(\"broadcastRampConfig\")\n      .withIndex(\"by_key\", (q) => q.eq(\"key\", \"current\"))\n      .unique();\n    if (!config) throw new Error(\"[_finalizeWaveRun] no broadcastRampConfig\");\n    if (config.pendingRunId !== runId) {\n      throw new Error(\n        `[_finalizeWaveRun] lost lease: expected ${runId}, found ${config.pendingRunId ?? \"<cleared>\"}. ` +\n        `Refusing to advance tier — operator force-released the lease, or another run took over.`,","sourceCodeStart":1402,"sourceCodeEnd":1438,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/broadcast/waveRuns.ts#L1402-L1438","documentation":"State-machine guard in _finalizeWaveRun: the run's status is not 'broadcast-created' (the actual status is embedded). Only a run whose broadcast was created may finalize to 'sent'; note that 'sent' is handled earlier as an idempotent no-op, so this throw means a genuinely unexpected status such as 'picking' or 'failed'.","triggerScenarios":"Thrown at convex/broadcast/waveRuns.ts:1420 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Read the embedded status: 'picking' or 'segment-created' means finalize ran before broadcast creation completed — re-run the finalize step in order","'failed' means the run was marked failed; recover via the documented recovery flow rather than forcing finalize"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}