{"record":{"id":"1c98c71bb3af0bf2","repo":"different-ai/openwork","slug":"openwork-server-did-not-report-an-access-token-aft","errorCode":null,"errorMessage":"OpenWork server did not report an access token after startup.","messagePattern":"OpenWork server did not report an access token after startup\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"apps/desktop/electron/main.mjs","lineNumber":1381,"sourceCode":"  if (runtimeDisposedForQuit || runtimeDisposeInProgress) return;\n  runtimeDisposeInProgress = true;\n  try {\n    await runtimeManager.dispose().catch(() => undefined);\n    runtimeDisposedForQuit = true;\n  } finally {\n    runtimeDisposeInProgress = false;\n  }\n}\n\nfunction assertOpenworkServerReady(info) {\n  if (!info?.running) {\n    throw new Error(\"OpenWork server did not stay running after startup.\");\n  }\n  if (!info.baseUrl) {\n    throw new Error(\"OpenWork server did not report a base URL after startup.\");\n  }\n  if (!info.ownerToken && !info.clientToken) {\n    throw new Error(\"OpenWork server did not report an access token after startup.\");\n  }\n  return info;\n}\n\nasync function bootRuntimeForSelectedWorkspace() {\n  if (typeof process.env.OPENWORK_EVAL_FATAL_DESKTOP_BOOTSTRAP_FAILURE === \"string\") {\n    throw new Error(process.env.OPENWORK_EVAL_FATAL_DESKTOP_BOOTSTRAP_FAILURE);\n  }\n  const list = await workspaceStore.readWorkspaceState();\n  const selectedId = list.selectedId || list.activeId || list.workspaces[0]?.id || \"\";\n  const workspace = selectedId\n    ? list.workspaces.find((entry) => entry?.id === selectedId)\n    : list.workspaces[0];\n  const workspaceRoot = String(workspace?.path ?? \"\").trim();\n  if (!workspaceRoot || workspace?.workspaceType === \"remote\") {\n    return { ok: true, skipped: true, reason: \"no-local-workspace\" };\n  }\n","sourceCodeStart":1363,"sourceCodeEnd":1399,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/main.mjs#L1363-L1399","documentation":"assertOpenworkServerReady validates the server-info object returned by the embedded OpenWork server after bootstrapping during Electron main-process startup. After confirming the server is running and reports a baseUrl, it requires at least one access credential — ownerToken or clientToken. If both are null/undefined the server came up unauthenticated/unreportable, so the app throws rather than continue without a usable token.","triggerScenarios":"The desktop bootstrap collects server info after the embedded openwork-server starts; if info.ownerToken and info.clientToken are both falsy (token file missing, server failed to mint/report a token, IPC returned the IDLE_OPENWORK_SERVER_INFO shape with nulls, or a race read the info before tokens were written), this throw fires at main.mjs:1381.","commonSituations":"Corrupted or wiped userData/credentials directory (token never persisted), stale server from a previous run occupying the state file, permissions preventing the token file read, server version mismatch writing a different info schema, or reading server info before token provisioning completed.","solutions":["Quit the app and clear/reset the userData directory (or the stale server state/token file) so a fresh token can be minted on next launch.","Ensure no stale openwork-server process is holding the old state file; kill leftover processes and relaunch.","Check file permissions on the userData path so the main process can read the token written by the server.","If reproducing in dev, run with a clean BLANK_SLATE/test profile so tokens are provisioned from scratch.","Update the desktop app and server together if a schema mismatch in server info is suspected."],"exampleFix":"// before: reading server info too early\nconst info = await getOpenworkServerInfo();\nassertOpenworkServerReady(info);\n// after: wait for the server to report tokens before asserting\nconst info = await waitForOpenworkServerInfo((i) => Boolean(i.ownerToken || i.clientToken));\nassertOpenworkServerReady(info);","handlingStrategy":"try-catch","validationCode":"const ready = info && info.baseUrl && (info.ownerToken || info.clientToken);\nif (!ready) throw new Error('server not ready: missing baseUrl or access token');","typeGuard":"function isServerInfoReady(info) {\n  return typeof info?.baseUrl === 'string' && info.baseUrl.length > 0 &&\n    (typeof info.ownerToken === 'string' && info.ownerToken.length > 0 ||\n     typeof info.clientToken === 'string' && info.clientToken.length > 0);\n}","tryCatchPattern":"try {\n  const info = await startOpenworkServer();\n} catch (err) {\n  if (err.message.includes('did not report an access token')) {\n    // clear stale userData/server state and retry bootstrap once\n    await resetServerState();\n    return retryBootstrap();\n  }\n  throw err;\n}","preventionTips":["Wait for a readiness signal (token present) before consuming server info instead of reading it once.","Keep server and desktop app versions in lockstep to avoid token-info schema drift.","Avoid wiping the userData directory while the server is running.","Log server-info shape (without token values) on bootstrap failure for faster diagnosis."],"tags":["electron","bootstrap","authentication","server-startup"],"backgroundTag":"missing-auth-token","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}