santifer/career-ops · error · Error

Apify did not return a run id

Error message

Apify did not return a run id: ${JSON.stringify(body).slice(0, 200)}

What it means

startRun posts to the Apify API to launch an actor run and expects the response body to contain a run id at body.data.id. If the response parses but lacks that field, the library cannot track or poll the run, so it throws with a truncated JSON dump (first 200 chars) of the unexpected body. This treats the malformed/failed response as an explicit error instead of returning undefined.

Solutions

  1. Inspect the truncated body in the message — it usually contains Apify's own error field explaining the failure (e.g. invalid token).
  2. Verify APIFY_TOKEN is valid and has run permission: `curl -H "Authorization: Bearer $APIFY_TOKEN" https://api.apify.com/v2/users/me`.
  3. Confirm the actorId is runnable by your account and the plan allows concurrent runs.
  4. Retry on transient 5xx/429 (the wrapper already retries connects; a hard API error body will still fail) or check the Apify status page.

Example fix

// before: silent default token
const token = process.env.APIFY_TOKEN; // undefined → error body in message
// after: fail fast with a clear message
if (!process.env.APIFY_TOKEN) throw new Error('missing APIFY_TOKEN');
const token = process.env.APIFY_TOKEN;
Defensive patterns

Strategy: try-catch

Validate before calling

if (!process.env.APIFY_TOKEN) throw new Error('missing APIFY_TOKEN before calling startRun');

Type guard

function hasRunId(body) {
  return !!body && typeof body === 'object' &&
    !!body.data && typeof body.data === 'object' &&
    typeof body.data.id === 'string' && body.data.id.length > 0;
}

Try / catch

try {
  const runId = await startRun(actorId, input);
} catch (err) {
  if (String(err.message).startsWith('Apify did not return a run id')) {
    // message embeds the first 200 chars of the error body — log or surface it
    console.error('Apify startRun failed:', err.message);
    // typically invalid token / plan limit / actor not runnable
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling startRun when the Apify API returns a 4xx/5xx error envelope (e.g. invalid token, insufficient plan, actor not found) wrapped as parseable JSON without data.id, or an unexpected response shape after an Apify API change.

Common situations: Expired or revoked APIFY_TOKEN; wrong token scope or plan limits; actor ID that exists but the account cannot run; transient Apify API incidents returning HTML/error JSON; proxying through a gateway that rewrites responses.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/83f52351c42822b3. Report an issue: GitHub.

Appendix: source

Thrown at plugins/apify/_apify.mjs:126

  throw lastErr;
}

async function startRun(actorId, input, token, deadline = null) {
  const url = `${APIFY_API_BASE}/acts/${normalizeActorId(actorId)}/runs`;
  const body = await fetchJson(
    url,
    {
      method: 'POST',
      headers: { 'content-type': 'application/json', ...authHeaders(token) },
      body: JSON.stringify(input || {}),
    },
    PER_REQUEST_TIMEOUT_MS,
    CONNECT_RETRY_ATTEMPTS,
    deadline,
  );
  const runId = body?.data?.id;
  if (!runId) {
    throw new Error(`Apify did not return a run id: ${JSON.stringify(body).slice(0, 200)}`);
  }
  return runId;
}

// Best-effort — if we give up on a run, stop the actor so credits aren't wasted.
async function abortRun(runId, token) {
  const url = `${APIFY_API_BASE}/actor-runs/${runId}/abort`;
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 5_000);
  try {
    await fetch(url, { method: 'POST', headers: authHeaders(token), signal: controller.signal });
  } catch {} finally {
    clearTimeout(timer);
  }
}

async function waitForRun(runId, token, deadline, timeoutMs) {
  const url = `${APIFY_API_BASE}/actor-runs/${runId}`;

View on GitHub (pinned to aac998c7ed)