HeyPuter/puter · critical · HttpError
unauthorized
unauthorized
Error message
Authentication required
What it means
NotificationDriver's #requireUserActor() pulls the actor from Context (ALS); if there is no actor at all, it throws 401 unauthorized. Every notification method runs this first, so an unauthenticated driver/call hits it immediately. This is distinct from the subsequent 403s for missing user or app-actor access.
Source
Thrown at src/backend/drivers/notification/NotificationDriver.ts:210
if (!uid)
throw new HttpError(400, 'Missing `uid`', {
legacyCode: 'bad_request',
});
const ok = await this.stores.notification.markAcknowledged(
uid,
actor.user.id,
);
return { success: ok };
}
// -- Permissions -------------------------------------------------
#requireUserActor(): Actor & {
user: { id: number; uuid: string; username: string };
} {
const actor = Context.get('actor') as Actor | undefined;
if (!actor)
throw new HttpError(401, 'Authentication required', {
legacyCode: 'unauthorized',
});
if (!actor.user?.id)
throw new HttpError(403, 'User actor required', {
legacyCode: 'forbidden',
});
// App-under-user actors are not allowed for notifications.
if (actor.app)
throw new HttpError(403, 'App actors cannot access notifications', {
legacyCode: 'forbidden',
});
return actor as Actor & {
user: { id: number; uuid: string; username: string };
};
}
// -- Serialization -----------------------------------------------
View on GitHub (pinned to 908ec23eda)
Solutions
- Ensure the request carries a valid user access token before calling notification methods.
- Re-authenticate / refresh the token if it has expired.
- For server-internal callers, set the actor in Context (or use a system token that resolves to one) before invoking the driver.
Example fix
// before — no token on the request
await fetch('/drivers/call', { method:'POST', body: JSON.stringify({ interface:'puter-notifications', method:'select' }) });
// after — attach a valid token
await fetch('/drivers/call', {
method:'POST',
headers: { Authorization:`Bearer ${token}` },
body: JSON.stringify({ interface:'puter-notifications', method:'select' }),
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure a valid token is attached before calling notification methods.
if (!token) {
throw new Error('authentication required for notifications');
}
await callWithAuth(token); Try / catch
try {
await notifications.select({});
} catch (e) {
if (e.status === 401 && e.code === 'unauthorized') {
// session expired — re-authenticate, then retry
await reauth();
} else throw e;
} Prevention
- Attach a valid user access token to every notification request.
- Handle 401 by re-authenticating rather than retrying with the same token.
- Notification methods reject app-actor tokens too — use a user token.
When it happens
Trigger: Calling any puter-notifications method (create/read/select/mark_shown/mark_acknowledged) without an authenticated session in the request context — e.g. a /drivers/call with no or invalid token, or an internal invocation that forgot to set the actor.
Common situations: Expired or missing access token; token not attached to the request; calling the driver directly server-side without establishing a Context actor; session timed out in the GUI.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/8ccf4e6cbcb431e3.
Report an issue: GitHub.