usememos/memos · error · ErrUnauthenticated
CodeUnauthenticated
CodeUnauthenticated
Error message
authentication required
What it means
ErrUnauthenticated is the sentinel error returned by the APIV1Service Authorizer when a request carries no valid credentials (missing, malformed, or expired Authorization header) and the target RPC does not allow anonymous access. The Connect interceptor maps it to CodeUnauthenticated and the gRPC-Gateway middleware maps it to HTTP 401, so both transports behave identically. It is thrown before any service logic runs.
Source
Thrown at server/router/api/v1/authz.go:15
package v1
import (
"context"
"errors"
"github.com/usememos/memos/internal/profile"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
// ErrUnauthenticated is returned by the Authorizer when a request must be rejected
// for lack of valid credentials. Each transport maps it to its own status code
// (Connect: CodeUnauthenticated, gRPC-Gateway: HTTP 401).
var ErrUnauthenticated = errors.New("authentication required")
// Authorizer is the single source of truth for method-level access control.
//
// It authenticates a request from its Authorization header and decides whether the
// (possibly anonymous) caller may reach a given RPC procedure. The Connect
// interceptor and the gRPC-Gateway middleware share one Authorizer so both
// transports enforce identical rules.
//
// Role-based authorization (admin checks) stays in the service layer; this type
// governs only authentication and anonymous access.
type Authorizer struct {
authenticator *auth.Authenticator
profile *profile.Profile
}
// NewAuthorizer creates an Authorizer backed by the given store, token secret, and
// instance profile.
func NewAuthorizer(store *store.Store, secret string, profile *profile.Profile) *Authorizer {View on GitHub (pinned to 14d757ce1f)
Solutions
- Attach a valid access token: Authorization: Bearer <access_token> obtained from SignIn/SignUp.
- If the token expired, call the refresh endpoint to get a new access token before retrying.
- If the endpoint should be reachable without login (e.g. GetInstanceStatus for the sign-in page), verify it is registered as an anonymous-allowed method in server/router/api/v1/acl_config.go.
- Check that the token value was not truncated or prefixed with extra characters when copied.
Example fix
// before
curl http://localhost:5230/memos.api.v1.MemoService/ListMemos \
-H 'Content-Type: application/json' -d '{}'
// after
curl http://localhost:5230/memos.api.v1.MemoService/ListMemos \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" -d '{}' Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure a token exists before calling a protected RPC
if (!accessToken) {
throw new Error('Sign in required before calling protected APIs');
} Try / catch
try {
const resp = await client.listMemos({});
} catch (e) {
if (e.code === 'unauthenticated') {
await signOut(); // clear stale token state, redirect to sign-in
return;
}
throw e;
} Prevention
- Always create Connect clients through the shared auth interceptor that attaches and refreshes the access token.
- Register the 401/unauthenticated handler to trigger a single token refresh then one retry, not an infinite loop.
- Keep truly public endpoints listed in acl_config.go so unauthenticated flows (sign-in page) never hit this error.
When it happens
Trigger: Calling any non-public RPC (e.g. MemoService.ListMemos, UserService.GetUser) without an Authorization header; sending an expired access token; sending a token with an unsupported scheme (the authenticator only accepts its defined schemes); or hitting a route not listed as anonymously accessible in acl_config.go.
Common situations: Frontend forgot to attach the access token via the Connect client interceptor; token expired and the refresh flow did not run (refresh token also expired or revoked); CLI/script integration using raw HTTP without Bearer auth; testing a locally protected route with curl.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to link account. Please sign in to Memos again and re
- password authentication for regular users cannot be disabled
- PAT not found
- PAT not found
- PAT not found
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/333167daa8ff41ab.
Report an issue: GitHub.