{"record":{"id":"3262c0e7e2bbf958","repo":"santifer/career-ops","slug":"apify-invalid-timeoutms-json-stringify-timeoutm","errorCode":null,"errorMessage":"apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must be a positive finite number of milliseconds)","messagePattern":"apify: invalid timeoutMs (.+?) \\(must be a positive finite number of milliseconds\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/apify/_apify.mjs","lineNumber":190,"sourceCode":"async 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  }\n  return await fetchDatasetItems(runId, token, deadline);\n}\n","sourceCodeStart":172,"sourceCodeEnd":203,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/plugins/apify/_apify.mjs#L172-L203","documentation":"Thrown by `runActor` (plugins/apify/_apify.mjs:190) when the `timeoutMs` option is not a positive finite number. The guard `if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)` rejects NaN, Infinity, negatives, zero, and non-numbers. This matters because timeoutMs is used to compute the shared deadline (`Date.now() + timeoutMs`) that gates startRun → waitForRun → fetchDatasetItems; an invalid value would produce a broken deadline (NaN or never-expiring).","triggerScenarios":"A portals.yml entry sets `timeout_ms` to a non-numeric string, 0, a negative number, or null; the value comes from a config parse that yields NaN; Infinity passed programmatically. The guard fires before deadline computation.","commonSituations":"YAML quoting turns a number into a string (`timeout_ms: \"180000\"`); a missing field defaulting oddly; a computed timeout that divides by zero yielding Infinity; passing seconds instead of ms and then clamping to 0.","solutions":["Set `timeout_ms` in portals.yml as a bare positive integer (milliseconds), e.g. `timeout_ms: 180000` (no quotes).","If computing the timeout, coerce and validate: `const ms = Number(raw); if (!Number.isFinite(ms) || ms <= 0) throw ...`.","Remember the unit is milliseconds, not seconds (default is 180_000 ms = 180s)."],"exampleFix":"# before — portals.yml (string → NaN coercion)\n- name: indeed\n  provider: apify\n  timeout_ms: \"180\"\n# after — bare positive integer in milliseconds\n- name: indeed\n  provider: apify\n  timeout_ms: 180000","handlingStrategy":"validation","validationCode":"function normalizeTimeoutMs(raw) {\n  const ms = Number(raw);\n  if (!Number.isFinite(ms) || ms <= 0) {\n    throw new Error(`Invalid timeout_ms '${raw}' — must be a positive integer in milliseconds.`);\n  }\n  return ms;\n}\nconst timeoutMs = normalizeTimeoutMs(entry.timeout_ms ?? 180000);","typeGuard":"/** @param {unknown} v */\nfunction isValidTimeoutMs(v) {\n  return typeof v === 'number' && Number.isFinite(v) && v > 0;\n}","tryCatchPattern":"try {\n  await runActor(actorId, input, { timeoutMs, token });\n} catch (err) {\n  if (/invalid timeoutMs/.test(err.message)) {\n    console.error(`Config error in portals.yml: ${err.message}`);\n  } else throw err;\n}","preventionTips":["Write `timeout_ms` as a bare YAML integer (no quotes), in milliseconds.","Validate numeric config fields with a schema (e.g. zod/ajv) at load time."],"tags":["apify","validation","configuration","timeout"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}