santifer/career-ops · error · Error
apify: invalid timeoutMs
Error message
apify: invalid timeoutMs ${JSON.stringify(timeoutMs)} (must be a positive finite number of milliseconds) What it means
Options validation in the Apify plugin's runActor: the timeoutMs option (defaulting to DEFAULT_RUN_TIMEOUT_MS) is not a positive finite number — it is NaN, negative, zero, Infinity, or a non-number. The interpolated JSON.stringify shows the exact bad value; the input at fault is the timeoutMs key of the options object passed by the caller (config plugins.yml or a wrapper).
Solutions
- Pass a positive finite number of ms, e.g. runActor(actorId, input, { timeoutMs: 30000 }).
- If reading from config, coerce with Number(value) and validate Number.isFinite before calling.
- Check units: the value is milliseconds; convert seconds with sec * 1000.
Example fix
// before
await runActor('user/actor', {}, { timeoutMs: config.timeout }); // '30' (string)
// after
const timeoutMs = Number(config.timeout) * 1000;
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error('bad timeout');
await runActor('user/actor', {}, { timeoutMs }); Defensive patterns
Strategy: validation
Validate before calling
const ms = Number(rawTimeoutMs);
if (!Number.isFinite(ms) || ms <= 0) throw new Error(`invalid timeoutMs: ${rawTimeoutMs}`); Type guard
const isValidTimeout = (v) => typeof v === 'number' && Number.isFinite(v) && v > 0;
Try / catch
try {
const items = await runActor(actorId, input, { timeoutMs });
} catch (e) {
if (String(e.message).startsWith('apify: invalid timeoutMs')) {
console.error('Fix timeout config; must be positive finite ms');
}
throw e;
} Prevention
- Coerce config values with Number() before passing numeric options.
- Standardize on milliseconds and convert seconds at the config boundary.
- Add a startup validation step for numeric config values.
- Avoid undefined fallbacks like Number(env.X) without a default.
When it happens
Trigger: Calling runActor with timeoutMs = 0, a negative number, NaN, Infinity, a string like '30000', or null/undefined coerced from bad config.
Common situations: Reading a timeout from YAML/JSON config where it comes in as a string; defaulting timeout from an env var that is unset (Number(undefined) = NaN); accidentally passing seconds instead of milliseconds (0 or negative after math).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- apify: entry has invalid field_map. Each of title, url…
- apify: entry missing 'actor' (e.g. misceres/indeed-scraper)
- apify: invalid actorId
- Apify run did not finish within s
- Report number must be a positive integer, got
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/3262c0e7e2bbf958.
Report an issue: GitHub.
Appendix: 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 aac998c7ed)