{"record":{"id":"83f52351c42822b3","repo":"santifer/career-ops","slug":"apify-did-not-return-a-run-id-json-stringify-bo","errorCode":null,"errorMessage":"Apify did not return a run id: ${JSON.stringify(body).slice(0, 200)}","messagePattern":"Apify did not return a run id: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/apify/_apify.mjs","lineNumber":126,"sourceCode":"  throw lastErr;\n}\n\nasync function startRun(actorId, input, token, deadline = null) {\n  const url = `${APIFY_API_BASE}/acts/${normalizeActorId(actorId)}/runs`;\n  const body = await fetchJson(\n    url,\n    {\n      method: 'POST',\n      headers: { 'content-type': 'application/json', ...authHeaders(token) },\n      body: JSON.stringify(input || {}),\n    },\n    PER_REQUEST_TIMEOUT_MS,\n    CONNECT_RETRY_ATTEMPTS,\n    deadline,\n  );\n  const runId = body?.data?.id;\n  if (!runId) {\n    throw new Error(`Apify did not return a run id: ${JSON.stringify(body).slice(0, 200)}`);\n  }\n  return runId;\n}\n\n// Best-effort — if we give up on a run, stop the actor so credits aren't wasted.\nasync function abortRun(runId, token) {\n  const url = `${APIFY_API_BASE}/actor-runs/${runId}/abort`;\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), 5_000);\n  try {\n    await fetch(url, { method: 'POST', headers: authHeaders(token), signal: controller.signal });\n  } catch {} finally {\n    clearTimeout(timer);\n  }\n}\n\nasync function waitForRun(runId, token, deadline, timeoutMs) {\n  const url = `${APIFY_API_BASE}/actor-runs/${runId}`;","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/plugins/apify/_apify.mjs#L108-L144","documentation":"startRun posts to the Apify API to launch an actor run and expects the response body to contain a run id at body.data.id. If the response parses but lacks that field, the library cannot track or poll the run, so it throws with a truncated JSON dump (first 200 chars) of the unexpected body. This treats the malformed/failed response as an explicit error instead of returning undefined.","triggerScenarios":"Calling startRun when the Apify API returns a 4xx/5xx error envelope (e.g. invalid token, insufficient plan, actor not found) wrapped as parseable JSON without data.id, or an unexpected response shape after an Apify API change.","commonSituations":"Expired or revoked APIFY_TOKEN; wrong token scope or plan limits; actor ID that exists but the account cannot run; transient Apify API incidents returning HTML/error JSON; proxying through a gateway that rewrites responses.","solutions":["Inspect the truncated body in the message — it usually contains Apify's own error field explaining the failure (e.g. invalid token).","Verify APIFY_TOKEN is valid and has run permission: `curl -H \"Authorization: Bearer $APIFY_TOKEN\" https://api.apify.com/v2/users/me`.","Confirm the actorId is runnable by your account and the plan allows concurrent runs.","Retry on transient 5xx/429 (the wrapper already retries connects; a hard API error body will still fail) or check the Apify status page."],"exampleFix":"// before: silent default token\nconst token = process.env.APIFY_TOKEN; // undefined → error body in message\n// after: fail fast with a clear message\nif (!process.env.APIFY_TOKEN) throw new Error('missing APIFY_TOKEN');\nconst token = process.env.APIFY_TOKEN;","handlingStrategy":"try-catch","validationCode":"if (!process.env.APIFY_TOKEN) throw new Error('missing APIFY_TOKEN before calling startRun');","typeGuard":"function hasRunId(body) {\n  return !!body && typeof body === 'object' &&\n    !!body.data && typeof body.data === 'object' &&\n    typeof body.data.id === 'string' && body.data.id.length > 0;\n}","tryCatchPattern":"try {\n  const runId = await startRun(actorId, input);\n} catch (err) {\n  if (String(err.message).startsWith('Apify did not return a run id')) {\n    // message embeds the first 200 chars of the error body — log or surface it\n    console.error('Apify startRun failed:', err.message);\n    // typically invalid token / plan limit / actor not runnable\n    return null;\n  }\n  throw err;\n}","preventionTips":["Verify APIFY_TOKEN with GET /v2/users/me before launching runs.","Log the truncated body from the message to read Apify's own error field.","Watch the Apify status page for incidents that change response envelopes."],"tags":["apify","api","unexpected-response","http"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}