{"record":{"id":"2ef8e0e930941d7f","repo":"koala73/worldmonitor","slug":"invalid-or-missing-jobid","errorCode":null,"errorMessage":"Invalid or missing jobId","messagePattern":"Invalid or missing jobId","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/scenario/v1/get-scenario-status.ts","lineNumber":131,"sourceCode":"    manifestFetchedAt: typeof c.manifestFetchedAt === 'string' ? c.manifestFetchedAt : '',\n    records: records.map(r => ({\n      iso2: r.iso2, hs2: r.hs2, state: r.state,\n      basis: r.state === 'evaluated' ? String(r.basis) : '',\n      rawImpact: r.state === 'evaluated' ? Number(r.rawImpact) : undefined,\n      fetchedAt: typeof r.fetchedAt === 'string' ? r.fetchedAt : '',\n    })),\n  };\n}\n\nexport async function getScenarioStatus(\n  ctx: ServerContext,\n  req: GetScenarioStatusRequest,\n): Promise<GetScenarioStatusResponse> {\n  await requirePremiumRpcAccess(ctx.request, ApiError, 'PRO subscription required');\n\n  const jobId = req.jobId ?? '';\n  if (!JOB_ID_RE.test(jobId)) {\n    throw new ValidationError([{ field: 'jobId', description: 'Invalid or missing jobId' }]);\n  }\n\n  // Worker writes under the raw (unprefixed) key, so we must read raw.\n  let envelope: WorkerResultEnvelope | null = null;\n  try {\n    envelope = await getRawJson(`scenario-result:${jobId}`) as WorkerResultEnvelope | null;\n  } catch {\n    throw new ApiError(502, 'Failed to fetch job status', '');\n  }\n\n  if (!envelope) {\n    return { status: 'pending', error: '' };\n  }\n\n  const status = typeof envelope.status === 'string' ? envelope.status : 'pending';\n\n  if (status === 'done') {\n    const result = coerceResult(envelope.result);","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/server/worldmonitor/scenario/v1/get-scenario-status.ts#L113-L149","documentation":"Request validation in getScenarioStatus: req.jobId does not match the JOB_ID_RE pattern (scenario:<timestamp>:<random-suffix> as generated by runScenario's generateJobId). A malformed or missing id is rejected before the premium access check proceeds to any cache read.","triggerScenarios":"Thrown at server/worldmonitor/scenario/v1/get-scenario-status.ts:70 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Pass the jobId exactly as returned by the runScenario response","Do not construct or truncate job ids client-side"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}