multica-ai/multica · warning

cloud pat verifier unavailable

Error message

cloud pat verifier unavailable

What it means

HTTP 503 returned when cloud PAT verification could not be completed because Cloud was unreachable, returned a 5xx, or the response failed to decode — anything other than an authoritative 'invalid' verdict. The middleware deliberately uses 503 rather than 401 so CLI/daemon callers retry instead of discarding a possibly valid token; the distinction is logged as 'cloud pat verify unavailable' at Warn level.

Source

Thrown at server/internal/middleware/auth.go:136

			// pass with a phantom X-User-ID.
			if strings.HasPrefix(tokenString, auth.CloudPATPrefix) {
				if cloudPAT == nil {
					slog.Warn("auth: mcn_ token presented but cloud verifier not configured", "path", r.URL.Path)
					http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
					return
				}
				identity, err := cloudPAT.Verify(r.Context(), tokenString, ownerLookupFor(queries))
				if err != nil {
					if errors.Is(err, auth.ErrCloudPATInvalid) {
						slog.Warn("auth: cloud rejected mcn_ token", "path", r.URL.Path, "error", err)
						http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
						return
					}
					// Cloud unreachable / 5xx / decode error. We surface
					// 503 so callers (CLI / daemon) can retry — a 401
					// here would tell them to throw out a valid token.
					slog.Warn("auth: cloud pat verify unavailable", "path", r.URL.Path, "error", err)
					http.Error(w, `{"error":"cloud pat verifier unavailable"}`, http.StatusServiceUnavailable)
					return
				}
				r.Header.Set("X-User-ID", identity.OwnerID)
				// Tag the auth path so account-level guards (e.g.
				// handler.RequireHumanActor on /api/cloud-billing/*)
				// can distinguish a cloud-node machine credential
				// from a human PAT/JWT. Mirrors the mat_ branch's
				// stamp of "task_token" — both are server-set,
				// authoritative, and stripped from any client-
				// supplied value at the top of this middleware. Same
				// rationale as MUL-2600: a machine credential
				// (running agent or running cloud node) must not be
				// treated as the owner having approved an account-
				// level action.
				r.Header.Set("X-Actor-Source", "cloud_pat")
				next.ServeHTTP(w, r)
				return
			}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Retry the request after a backoff — the token is not invalidated by this response.
  2. Check server → cloud connectivity (curl the cloud verify endpoint from the server host).
  3. Inspect firewall/DNS/proxy config for the cloud host on the server.
  4. If persistent, compare server and cloud versions for response-format skew and check cloud status page.

Example fix

// before: treat any non-2xx as bad token and log out
if resp.StatusCode != 200 { logout() }

// after: 503 means retry, 401 means re-auth
if resp.StatusCode == http.StatusServiceUnavailable {
    time.Sleep(backoff.Next()); retry(req)
} else if resp.StatusCode == http.StatusUnauthorized {
    refreshToken()
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight cloud reachability before a batch of authenticated calls
if err := pingCloudVerify(ctx, 2*time.Second); err != nil {
    return fmt.Errorf("cloud verifier unreachable, delaying batch: %w", err)
}

Try / catch

var bo backoff.ExponentialBackOff
for attempt := 0; attempt < 5; attempt++ {
    resp, err := client.Do(req)
    if err == nil && resp.StatusCode != http.StatusServiceUnavailable { break }
    time.Sleep(bo.NextAttempt())
}

Prevention

When it happens

Trigger: Cloud verify endpoint is down or timing out; network egress blocked from the server; TLS failure to the cloud host; malformed/unexpected response body from Cloud (version skew).

Common situations: Cloud outage; firewall/DNS problems on self-hosted servers; transient 5xx during cloud deploys; proxy misconfiguration intercepting cloud traffic.

Related errors


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