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

  1. Set `timeout_ms` in portals.yml as a bare positive integer (milliseconds), e.g. `timeout_ms: 180000` (no quotes).
  2. If computing the timeout, coerce and validate: `const ms = Number(raw); if (!Number.isFinite(ms) || ms <= 0) throw ...`.
  3. 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

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

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/3262c0e7e2bbf958. Report an issue: GitHub.