santifer/career-ops · error · Error
apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must
Error message
apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must be a positive finite number of milliseconds) What it means
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).
Source
Thrown at plugins/apify/_apify.mjs:190
async function fetchDatasetItems(runId, token, deadline = null) {
const url = `${APIFY_API_BASE}/actor-runs/${runId}/dataset/items`;
const items = await fetchJson(
url,
{ headers: authHeaders(token) },
PER_REQUEST_TIMEOUT_MS * 2,
CONNECT_RETRY_ATTEMPTS,
deadline,
);
if (!Array.isArray(items)) {
throw new Error(`Apify run ${runId} returned non-array dataset payload`);
}
return items;
}
export async function runActor(actorId, input, { timeoutMs = DEFAULT_RUN_TIMEOUT_MS, token = process.env.APIFY_TOKEN } = {}) {
if (!token) throw new Error('APIFY_TOKEN not set');
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
throw new Error(`apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must be a positive finite number of milliseconds)`);
}
// Single deadline shared across startRun → waitForRun → fetchDatasetItems so
// the caller's timeoutMs is the end-to-end ceiling, not just the wait loop.
const deadline = Date.now() + timeoutMs;
const runId = await startRun(actorId, input, token, deadline);
const run = await waitForRun(runId, token, deadline, timeoutMs);
if (run.status !== 'SUCCEEDED') {
const reason = run.statusMessage ? `: ${run.statusMessage}` : '';
throw new Error(`Apify actor ${actorId} finished with status ${run.status}${reason}`);
}
return await fetchDatasetItems(runId, token, deadline);
}
View on GitHub (pinned to 9b17a8ac97)
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).
Example fix
# before — portals.yml (string → NaN coercion) - name: indeed provider: apify timeout_ms: "180" # after — bare positive integer in milliseconds - name: indeed provider: apify timeout_ms: 180000
Defensive patterns
Strategy: validation
Validate before calling
function normalizeTimeoutMs(raw) {
const ms = Number(raw);
if (!Number.isFinite(ms) || ms <= 0) {
throw new Error(`Invalid timeout_ms '${raw}' — must be a positive integer in milliseconds.`);
}
return ms;
}
const timeoutMs = normalizeTimeoutMs(entry.timeout_ms ?? 180000); Type guard
/** @param {unknown} v */
function isValidTimeoutMs(v) {
return typeof v === 'number' && Number.isFinite(v) && v > 0;
} Try / catch
try {
await runActor(actorId, input, { timeoutMs, token });
} catch (err) {
if (/invalid timeoutMs/.test(err.message)) {
console.error(`Config error in portals.yml: ${err.message}`);
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- apify: invalid actorId ${JSON.stringify(actorId)}. Expected
- apify: entry ${entry.name} missing 'actor' (e.g. misceres/in
- apify: entry ${entry.name} has invalid field_map. Each of ti
- Invalid page budget "${maxPages}". Use a positive integer.
- CV is ${pageCount} ${actualLabel}; the allowed maximum is ${
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/3262c0e7e2bbf958.
Report an issue: GitHub.