santifer/career-ops · error · Error
Apify did not return a run id: ${JSON.stringify(body).slice(
Error message
Apify did not return a run id: ${JSON.stringify(body).slice(0, 200)} What it means
Thrown by `startRun` (plugins/apify/_apify.mjs:126) after a successful POST to `/acts/<actor>/runs` when the JSON response body has no `data.id` field. Apify's start-run endpoint is expected to return `{ data: { id: '<runId>' } }`; an absent or malformed `data.id` means the run was not actually created (or the response shape changed), so the caller cannot poll for results. The first 200 chars of the body are included for diagnosis.
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 9b17a8ac97)
Solutions
- Inspect the 200-char body snippet in the message — it usually reveals whether it is an error envelope or a schema change.
- Verify the actorId is valid and the actor still exists on the Apify store.
- Check Apify status page / your account plan limits (a soft failure can omit the run id).
- Retry once; if persistent, update the plugin if the Apify v2 response schema changed.
Defensive patterns
Strategy: try-catch
Try / catch
try {
const items = await runActor(actorId, input, opts);
} catch (err) {
if (/did not return a run id/.test(err.message)) {
// Inspect the body snippet; likely API drift or a soft platform error.
console.error(`Apify start-run returned no id — check actor/account: ${err.message}`);
// retry once after a short delay
} else throw err;
} Prevention
- Keep the apify plugin updated to track Apify v2 response schema.
- Watch the 200-char body snippet in the message for early signs of API drift.
When it happens
Trigger: Apify returns 200 but with an unexpected body (e.g. an error envelope without `data`, an HTML error page parsed as JSON, or an API-version mismatch where the id lives elsewhere). The check `const runId = body?.data?.id; if (!runId)` fires.
Common situations: Apify API version drift (response schema changed); hitting a deprecated actor endpoint; a transient Apify platform error returning a 200 with an error body; an actor that requires paid plan but returns a soft error; an intercepting proxy returning a captive-portal JSON.
Related errors
- Apify run ${runId} returned non-array dataset payload
- [models] Failed to fetch free model list: ${reason}. Check t
- Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s
- Timeout after ${MODEL_TIMEOUT_MS / 1000}s
- All ${active.length} active models failed. Last error: ${las
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/83f52351c42822b3.
Report an issue: GitHub.