multica-ai/multica · error
invalid token
Error message
invalid token
What it means
HTTP 401 from the mat_ (agent task token) branch of the auth middleware when the token prefix matches agent task tokens but the middleware was constructed with a nil database queries object, so the token hash cannot be looked up at all. This is a server-side wiring defect, not a bad token: the handler simply has no way to validate task tokens.
Source
Thrown at server/internal/middleware/auth.go:75
if fromCookie && !auth.ValidateCSRF(r) {
slog.Debug("auth: CSRF validation failed", "path", r.URL.Path)
http.Error(w, `{"error":"CSRF validation failed"}`, http.StatusForbidden)
return
}
// Agent task token: "mat_" prefix. Minted by the server at
// task-claim time and injected by the daemon into the agent
// process. Authoritative for actor identity — the bound
// (user_id, agent_id, task_id, workspace_id) triple is
// written into request headers here, OVERRIDING whatever the
// client sent, so a downstream actor-resolver cannot be
// tricked by a client that strips or forges X-Agent-ID /
// X-Task-ID. Human-only endpoints (e.g. agent env
// management) reject requests authenticated this way; see
// `actorSourceFromRequest`. MUL-2600.
if strings.HasPrefix(tokenString, "mat_") {
if queries == nil {
http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
return
}
hash := auth.HashToken(tokenString)
tt, err := queries.GetTaskTokenByHash(r.Context(), hash)
if err != nil {
slog.Warn("auth: invalid task token", "path", r.URL.Path, "error", err)
http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
return
}
r.Header.Set("X-User-ID", uuidToString(tt.UserID))
r.Header.Set("X-Agent-ID", uuidToString(tt.AgentID))
r.Header.Set("X-Task-ID", uuidToString(tt.TaskID))
r.Header.Set("X-Workspace-ID", uuidToString(tt.WorkspaceID))
// X-Actor-Source flags the auth path so resolveActor and
// any owner-only handler can deny without re-querying the
// token table. The value "task_token" is the only signal
// this header is allowed to carry — strip anything else a
// client tried to send.View on GitHub (pinned to 2c0912b6ec)
Solutions
- Fix the server wiring: construct the auth middleware with a non-nil queries object wherever mat_ tokens must be accepted.
- If you run the server, check startup logs for database connection failure — queries is nil when DB init failed.
- If you are the client, retry against the main API endpoint, not a DB-less auxiliary listener.
Example fix
// before: middleware without DB queries r.Use(middleware.Auth(nil, nil)) // after: provide queries r.Use(middleware.Auth(queries, cfg))
Defensive patterns
Strategy: validation
Try / catch
resp, err := client.Do(req)
if err == nil && resp.StatusCode == 401 && isTaskTokenEnv {
// nil-queries is a server wiring bug: report to operator; re-claim may mint a fresh token anyway
} Prevention
- Construct the auth middleware with non-nil DB queries wherever mat_ tokens are accepted.
- Add a startup check that fails if task-token auth is reachable without a DB.
- Cover the mat_ path in integration tests with a real queries object.
When it happens
Trigger: A request with an Authorization: Bearer mat_... token reaches an auth middleware instance built without database queries (e.g. a misconfigured router, a test server, or an embedded deployment that skipped DB wiring).
Common situations: Integration tests constructing the middleware without a DB; a partially initialized server during startup races; deployments that intentionally run DB-less (metrics/health) but accidentally route API traffic through them.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- missing authorization
- invalid claims
- errMsg
- Invalid desktop runtime config JSON: ${err instanceof Error
- cloud pat invalid
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/d4c3924f71492d31.
Report an issue: GitHub.