multica-ai/multica · error · ErrCloudPATInvalid

cloud pat invalid

Error message

cloud pat invalid

What it means

Sentinel error ErrCloudPATInvalid in server/internal/auth/cloud_pat.go: the Fleet (cloud) verification endpoint answered definitively that the presented mcn_ personal access token is invalid. Per the doc comment, the Auth/DaemonAuth middlewares map it to HTTP 401. It means the token reached Fleet and was rejected — expired, revoked, or malformed — as opposed to Fleet being unreachable (ErrCloudPATUnavailable, 503).

Source

Thrown at server/internal/auth/cloud_pat.go:82

// cloudPATDefaultTimeout is the per-request HTTP timeout for verify
// calls when the caller doesn't supply an *http.Client. Auth must
// stay snappy: Fleet should answer in tens of milliseconds, and a
// hung verify would block every incoming request behind it. Tighter
// than cloudruntime's 35s because that one proxies arbitrary user
// traffic; this one only ever sees a small JSON exchange.
const cloudPATDefaultTimeout = 5 * time.Second

// Verifier sentinel errors. Callers (the Auth / DaemonAuth middlewares)
// branch on these to map cloud outcomes onto HTTP status codes:
//
//   - ErrCloudPATInvalid       → 401 (Fleet says token is bad)
//   - ErrCloudPATUnavailable   → 503 (Fleet unreachable / 5xx)
//   - ErrCloudPATNotConfigured → 401 (server has no Fleet URL set; we
//     don't reveal that mcn_ is "supported but disabled" — failing
//     closed avoids treating misconfigured prod the same as enabled)
var (
	ErrCloudPATInvalid       = errors.New("cloud pat invalid")
	ErrCloudPATUnavailable   = errors.New("cloud pat verifier unavailable")
	ErrCloudPATNotConfigured = errors.New("cloud pat verifier not configured")
)

// CloudPATIdentity is what a successful verify resolves to. We keep
// only the fields the auth path actually needs:
//
//   - OwnerID is the user whose request this is (mapped to X-User-ID).
//   - InstanceID / InstanceRecordID are recorded so downstream code can
//     correlate the request with a specific cloud node; they are not
//     used for authorization today, but stashing them now keeps the
//     wire shape stable for callers that later want to assert a
//     particular instance binding.
//
// We deliberately drop token_last4, status, issued_at, etc. — those
// are diagnostic fields that don't belong in cached auth state.
type CloudPATIdentity struct {
	OwnerID          string `json:"o"`

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Generate a new PAT in the cloud console and replace the stored credential, then retry.
  2. Verify the token string was copied whole (no leading/trailing whitespace or shell-quoting artifacts).
  3. Confirm the Fleet URL configured on the server matches the environment the token was issued in.
  4. Distinguish from 503/unavailable: a 401 means Fleet answered, so do not retry the same token.
Defensive patterns

Strategy: try-catch

Try / catch

if errors.Is(err, auth.ErrCloudPATInvalid) {
    http.Error(w, "cloud token rejected; re-authenticate", http.StatusUnauthorized)
    return
}

Prevention

When it happens

Trigger: Request with an mcn_ PAT that Fleet no longer accepts (revoked in the cloud console, expired); token truncated or with stray whitespace/quotes when pasted; token issued for a different Fleet instance than the configured URL.

Common situations: User rotated their cloud PAT but the server config or CLI still holds the old one; copy/paste damage when storing the token; environment pointing at a different Fleet environment (staging vs prod) than where the token was created.

Related errors


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