{"record":{"id":"4e523a4826d2d9f7","repo":"multica-ai/multica","slug":"mint-pat-response-missing-token","errorCode":null,"errorMessage":"mint PAT: response missing token","messagePattern":"mint PAT: response missing token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/src/main/daemon-manager.ts","lineNumber":627,"sourceCode":"      \"Content-Type\": \"application/json\",\n      Authorization: `Bearer ${jwt}`,\n    },\n    // Omit expires_in_days → server treats as null → non-expiring PAT.\n    body: JSON.stringify({ name: \"Multica Desktop\" }),\n  });\n  if (!res.ok) {\n    const body = await res.text().catch(() => \"\");\n    // Attach the status so callers can tell a genuine auth rejection (401 — the\n    // session token is dead) apart from a transient failure (5xx, etc.) without\n    // string-matching the message.\n    throw Object.assign(\n      new Error(`mint PAT failed: ${res.status} ${res.statusText} ${body}`),\n      { status: res.status },\n    );\n  }\n  const data = (await res.json()) as { token?: unknown };\n  if (typeof data.token !== \"string\" || !data.token.startsWith(\"mul_\")) {\n    throw new Error(\"mint PAT: response missing token\");\n  }\n  return data.token;\n}\n\n/**\n * Ensure the active profile's config.json has a usable token for the daemon.\n *\n * - Input from the renderer is the user's JWT (from localStorage) plus the\n *   current user's id, so we can detect session changes.\n * - If the profile already has a cached PAT (`mul_...`) AND the sidecar user\n *   id matches the caller, reuse it — minting fresh on every launch would\n *   accumulate garbage in the user's tokens page.\n * - On user mismatch (or first run) call POST /api/tokens with the JWT to\n *   mint a fresh PAT, overwriting any stale cached PAT. This is the critical\n *   path: without it, a previous user's PAT would be used by a new session.\n * - If the caller happens to pass a PAT directly, write it through.\n * - When we mint fresh and a daemon is already running, restart it so the\n *   new credentials take effect (the Go daemon reads config at startup).","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/apps/desktop/src/main/daemon-manager.ts#L609-L645","documentation":"Thrown by prepareHermesHome when os.MkdirAll cannot create the per-task overlay directory <envRoot>/hermes-home with mode 0700. The overlay holds the mirrored shared home plus a derived config that can contain inline api_key secrets, which is why the daemon insists on creating it with tight permissions.","triggerScenarios":"prepareHermesHome runs and MkdirAll(hermesHome, 0o700) fails: a parent in envRoot is not writable by the daemon user, a non-directory file already exists somewhere along the path, the filesystem is full or read-only, or a symlink loop exists.","commonSituations":"env-root created by root while the daemon runs unprivileged; a leftover file named 'hermes-home' from an aborted run; disk exhaustion on the task volume; read-only container layer where env-root was not mounted as writable.","solutions":["Inspect the wrapped *PathError for the failing path; ensure every parent directory is writable and owned by the daemon user.","Remove any stale non-directory file occupying the hermes-home path.","Free disk space or remount the env-root volume read-write.","Restart the daemon as the user that owns the env-root (or chown the env-root to the daemon user)."],"exampleFix":"# before\n$ ls -l /var/lib/app/envs/task-9/hermes-home\n-rw-r--r-- 1 root root 0 ... hermes-home   # stale file blocks MkdirAll\n\n# after\n$ rm /var/lib/app/envs/task-9/hermes-home && chown -R daemonuser /var/lib/app/envs","handlingStrategy":"try-catch","validationCode":"if err := checkWritable(filepath.Dir(envRoot)); err != nil {\n    return fmt.Errorf(\"env-root parent not writable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := prepareHermesHome(...); err != nil {\n    if strings.Contains(err.Error(), \"create hermes-home dir\") {\n        var pe *os.PathError\n        if errors.As(err, &pe) {\n            // pe.Path names the failing component; fix perms/space, retry once\n        }\n    }\n}","preventionTips":["Ensure the daemon user owns env-root directories.","Disk-space monitoring on the env-root volume.","GC stale per-task env-roots so MkdirAll never hits stray files."],"tags":["go","hermes","execenv","filesystem","permissions"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}