{"record":{"id":"b2729b4610d17ea1","repo":"santifer/career-ops","slug":"apify-run-runid-returned-non-array-dataset-payl","errorCode":null,"errorMessage":"Apify run ${runId} returned non-array dataset payload","messagePattern":"Apify run (.+?) returned non-array dataset payload","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/apify/_apify.mjs","lineNumber":182,"sourceCode":"    if (sleepMs > 0) await sleep(sleepMs);\n  }\n  // Fire-and-forget cleanup; don't add abortRun's 5s to our wall-clock budget.\n  void abortRun(runId, token).catch(() => {});\n  const suffix = lastError ? ` (last error: ${lastError.message})` : '';\n  throw new Error(`Apify run ${runId} did not finish within ${Math.round(timeoutMs / 1000)}s${suffix}`);\n}\n\nasync function fetchDatasetItems(runId, token, deadline = null) {\n  const url = `${APIFY_API_BASE}/actor-runs/${runId}/dataset/items`;\n  const items = await fetchJson(\n    url,\n    { headers: authHeaders(token) },\n    PER_REQUEST_TIMEOUT_MS * 2,\n    CONNECT_RETRY_ATTEMPTS,\n    deadline,\n  );\n  if (!Array.isArray(items)) {\n    throw new Error(`Apify run ${runId} returned non-array dataset payload`);\n  }\n  return items;\n}\n\nexport async function runActor(actorId, input, { timeoutMs = DEFAULT_RUN_TIMEOUT_MS, token = process.env.APIFY_TOKEN } = {}) {\n  if (!token) throw new Error('APIFY_TOKEN not set');\n  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n    throw new Error(`apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must be a positive finite number of milliseconds)`);\n  }\n  // Single deadline shared across startRun → waitForRun → fetchDatasetItems so\n  // the caller's timeoutMs is the end-to-end ceiling, not just the wait loop.\n  const deadline = Date.now() + timeoutMs;\n  const runId = await startRun(actorId, input, token, deadline);\n  const run = await waitForRun(runId, token, deadline, timeoutMs);\n  if (run.status !== 'SUCCEEDED') {\n    const reason = run.statusMessage ? `: ${run.statusMessage}` : '';\n    throw new Error(`Apify actor ${actorId} finished with status ${run.status}${reason}`);\n  }","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/plugins/apify/_apify.mjs#L164-L200","documentation":"fetchDatasetItems fetches dataset items for a completed Apify run via the Apify API. The library expects the API (or a retry-wrapped fetch helper) to return an array of items; if the parsed payload is not an array, it throws this error to signal an unexpected response shape rather than passing garbage downstream.","triggerScenarios":"Calling runActor/fetchDatasetItems when the Apify API returns a non-array dataset payload — e.g. an error object, null, or an object with items nested elsewhere (API schema change, run whose default dataset is empty and returned as null, or an auth/rate-limit error body parsed as JSON).","commonSituations":"Apify API changes the dataset endpoint response envelope; actor runs that produce no dataset; expired/invalid token causing an error JSON body instead of an items array; network proxy returning JSON error payloads.","solutions":["Re-run the actor and verify it produces a default dataset with items (check run detail in Apify console).","Verify APIFY_TOKEN is valid and not rate-limited; a 401/429 body parsed as JSON is not an array.","Log the raw payload before this point to see the actual shape (e.g. {data:{items:[...]}}).","Check for Apify API version/schema changes and update the plugin's dataset fetch to unwrap the correct field.","Wrap the call in try/catch and fall back to a direct GET of runId's dataset endpoint with ?clean=true."],"exampleFix":"// before\nconst items = await res.json();\nreturn items;\n// after\nconst payload = await res.json();\nconst items = Array.isArray(payload) ? payload : (Array.isArray(payload?.data?.items) ? payload.data.items : []);","handlingStrategy":"type-guard","validationCode":"const items = await fetchDatasetItems(runId, token, deadline);\nif (!Array.isArray(items)) throw new TypeError('dataset payload is not an array');","typeGuard":"function isDatasetItems(v) { return Array.isArray(v) && v.every(i => i && typeof i === 'object'); }","tryCatchPattern":"try {\n  const items = await runActor(actorId, input, { token, timeoutMs: 60000 });\n} catch (e) {\n  if (String(e.message).includes('non-array dataset payload')) {\n    // inspect raw API response / re-run actor\n  } else throw e;\n}","preventionTips":["Verify the actor's default dataset contains items before relying on fetchDatasetItems.","Keep APIFY_TOKEN valid to avoid error bodies being parsed as payloads.","Pin/monitor Apify API changes; unwrap nested envelopes defensively.","Log raw payloads in development to catch shape changes early."],"tags":["apify","api","unexpected-response-shape","dataset"],"backgroundTag":"unexpected-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"}