multica-ai/multica · error · Error

mint PAT: response missing token

Error message

mint PAT: response missing token

What it means

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.

Source

Thrown at apps/desktop/src/main/daemon-manager.ts:627

      "Content-Type": "application/json",
      Authorization: `Bearer ${jwt}`,
    },
    // Omit expires_in_days → server treats as null → non-expiring PAT.
    body: JSON.stringify({ name: "Multica Desktop" }),
  });
  if (!res.ok) {
    const body = await res.text().catch(() => "");
    // Attach the status so callers can tell a genuine auth rejection (401 — the
    // session token is dead) apart from a transient failure (5xx, etc.) without
    // string-matching the message.
    throw Object.assign(
      new Error(`mint PAT failed: ${res.status} ${res.statusText} ${body}`),
      { status: res.status },
    );
  }
  const data = (await res.json()) as { token?: unknown };
  if (typeof data.token !== "string" || !data.token.startsWith("mul_")) {
    throw new Error("mint PAT: response missing token");
  }
  return data.token;
}

/**
 * Ensure the active profile's config.json has a usable token for the daemon.
 *
 * - Input from the renderer is the user's JWT (from localStorage) plus the
 *   current user's id, so we can detect session changes.
 * - If the profile already has a cached PAT (`mul_...`) AND the sidecar user
 *   id matches the caller, reuse it — minting fresh on every launch would
 *   accumulate garbage in the user's tokens page.
 * - On user mismatch (or first run) call POST /api/tokens with the JWT to
 *   mint a fresh PAT, overwriting any stale cached PAT. This is the critical
 *   path: without it, a previous user's PAT would be used by a new session.
 * - If the caller happens to pass a PAT directly, write it through.
 * - When we mint fresh and a daemon is already running, restart it so the
 *   new credentials take effect (the Go daemon reads config at startup).

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Inspect the wrapped *PathError for the failing path; ensure every parent directory is writable and owned by the daemon user.
  2. Remove any stale non-directory file occupying the hermes-home path.
  3. Free disk space or remount the env-root volume read-write.
  4. Restart the daemon as the user that owns the env-root (or chown the env-root to the daemon user).

Example fix

# before
$ ls -l /var/lib/app/envs/task-9/hermes-home
-rw-r--r-- 1 root root 0 ... hermes-home   # stale file blocks MkdirAll

# after
$ rm /var/lib/app/envs/task-9/hermes-home && chown -R daemonuser /var/lib/app/envs
Defensive patterns

Strategy: try-catch

Validate before calling

if err := checkWritable(filepath.Dir(envRoot)); err != nil {
    return fmt.Errorf("env-root parent not writable: %w", err)
}

Try / catch

if err := prepareHermesHome(...); err != nil {
    if strings.Contains(err.Error(), "create hermes-home dir") {
        var pe *os.PathError
        if errors.As(err, &pe) {
            // pe.Path names the failing component; fix perms/space, retry once
        }
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/4e523a4826d2d9f7. Report an issue: GitHub.