{"record":{"id":"0a3aa7db83dd4adc","repo":"NousResearch/hermes-agent","slug":"label-exited-and-dashboardindexurl-baseurl","errorCode":null,"errorMessage":"${label} exited and ${dashboardIndexUrl(baseUrl)} is served by a process we did not spawn; refusing its session token.","messagePattern":"(.+?) exited and (.+?) is served by a process we did not spawn; refusing its session token\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/dashboard-token.ts","lineNumber":96,"sourceCode":" */\nfunction isForeignBackendToken({ servedToken, spawnToken, childAlive }) {\n  return Boolean(servedToken) && servedToken !== spawnToken && !childAlive\n}\n\n/**\n * Resolve the token the backend actually serves, adopting benign drift and\n * failing loudly on a foreign backend. `childAlive` is a thunk so liveness is\n * sampled after the fetch, not before.\n */\nasync function adoptServedDashboardToken(baseUrl, spawnToken, { childAlive, label = 'Hermes backend', ...options }) {\n  const servedToken = await resolveServedDashboardToken(baseUrl, spawnToken, options).catch(error => {\n    options.rememberLog?.(`[boot] could not read served dashboard token (${label}): ${error.message}`)\n\n    return spawnToken\n  })\n\n  if (isForeignBackendToken({ servedToken, spawnToken, childAlive: childAlive() })) {\n    throw new Error(\n      `${label} exited and ${dashboardIndexUrl(baseUrl)} is served by a process we did not spawn; refusing its session token.`\n    )\n  }\n\n  return servedToken\n}\n\nexport {\n  adoptServedDashboardToken,\n  dashboardIndexUrl,\n  DEFAULT_TOKEN_FETCH_TIMEOUT_MS,\n  extractInjectedDashboardToken,\n  fetchPublicText,\n  isForeignBackendToken,\n  resolveServedDashboardToken\n}\n","sourceCodeStart":78,"sourceCodeEnd":113,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/dashboard-token.ts#L78-L113","documentation":"ValueError from update_job (cron/jobs.py:1865): the updates dict contains a field in _IMMUTABLE_JOB_FIELDS (currently just 'id'). 'id' is a filesystem path component under OUTPUT_DIR — allowing it to change would leak path-escape values into output writes/deletes, so mutation is blocked before any other update logic runs.","triggerScenarios":"update_job('a1b2c3d4', {'id': 'new-id'}); merging a full job dict back as the updates payload (it contains the id key); a generic 'save object' helper that round-trips every field including immutable ones.","commonSituations":"Load-modify-save patterns that submit the whole record instead of a diff; attempting to 'rename' a job by changing its id; bulk-import scripts reusing job dicts from another instance.","solutions":["Remove 'id' (and any other listed field) from the updates dict — never change a job's identity.","To 'rename', update the 'name' field instead; to replace a job, create a new one and delete the old.","Strip immutable keys programmatically before calling update_job."],"exampleFix":"# before\nupdate_job(job[\"id\"], job)  # full record, includes 'id'\n\n# after\nupdates = {k: v for k, v in desired.items() if k != \"id\"}\nupdate_job(job[\"id\"], updates)","handlingStrategy":"validation","validationCode":"IMMUTABLE = {\"id\"}\n\ndef sanitize_updates(updates: dict) -> dict:\n    return {k: v for k, v in (updates or {}).items() if k not in IMMUTABLE}\n\nupdate_job(job_id, sanitize_updates(desired_changes))","typeGuard":null,"tryCatchPattern":"try:\n    update_job(job_id, updates)\nexcept ValueError as e:\n    if \"cannot be updated\" in str(e):\n        update_job(job_id, sanitize_updates(updates))  # strip immutable fields, retry\n        raise","preventionTips":["Always send a diff to update_job, never the whole loaded job record.","To change a job's identity, create a new job and delete the old one."],"tags":["cron","immutability","security","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}