HeyPuter/puter · error · HttpError
unauthorized
unauthorized
Error message
Unauthorized
What it means
Thrown by FSController.#requireActor when `req.actor` is falsy at handler entry. The actor is set by upstream authentication middleware (session or app token), so reaching a handler without it means authn never ran or failed silently and the request slipped past the route gate. Every mutating FS handler opens with this check, so it is the first thing that fires when the actor context is absent.
Source
Thrown at src/backend/controllers/fs/FSController.ts:1604
await this.#assertAccess(actor, target.path, 'read');
await this.#assertAccess(actor, parent.path, 'write');
const shortcut = await this.services.fs.mkshortcut(userId, {
parent,
name,
target,
dedupeName: this.#toBoolean(body.dedupe_name) ?? true,
});
this.#emitGuiItemAdded(shortcut);
res.json(this.#toClientEntry(shortcut));
}
// -- Read-side helpers -----------------------------------------------
#requireActor(req: Request): Actor {
const actor = req.actor;
if (!actor) {
throw new HttpError(401, 'Unauthorized', {
legacyCode: 'unauthorized',
});
}
return actor;
}
#isRootPathRef(source: Record<string, unknown>): boolean {
if (typeof source.path !== 'string') return false;
if (source.uid !== undefined || source.uuid !== undefined) return false;
if (source.id !== undefined) return false;
return source.path.trim() === '/';
}
async #resolveEntryForRequest(source: Record<string, unknown>) {
const mod = await import('../../services/fs/resolveNode.js');
const username = Context.get('actor')?.user?.username;
const rawPath =
typeof source.path === 'string' ? source.path : undefined;View on GitHub (pinned to 908ec23eda)
Solutions
- Send a valid session token or app access token in the request (Authorization header or puter-auth cookie).
- Verify the auth middleware is mounted on the api subdomain route and runs before FSController.
- Confirm the token has not expired and the user/app it represents still exists.
- If reproducing in tests, use setupPuterTestEnv() helpers that authenticate the request rather than calling the handler with a bare Request.
Example fix
// before
await fetch('https://api.puter.com/mkshortcut', { method:'POST', body: JSON.stringify({...}) });
// after
await fetch('https://api.puter.com/mkshortcut', { method:'POST', headers:{ Authorization:`Bearer ${token}` }, body: JSON.stringify({...}) }); Defensive patterns
Strategy: validation
Validate before calling
function withAuth(token, init = {}) {
if (!token) throw new Error('Auth token required for FS mutation');
return { ...init, headers: { ...(init.headers||{}), Authorization: `Bearer ${token}` } };
} Type guard
/** @param {any} req */
function hasActor(req) { return !!req?.actor; } Try / catch
try { await api(); }
catch (e) {
if (e?.status === 401) { /* redirect to login / refresh token */ }
else throw e;
} Prevention
- Always attach Authorization on FS mutation calls; gate them on a resolved token.
- Use the SDK's session-aware client rather than hand-rolled fetch.
- In tests, drive requests through the authenticated test-env helpers.
When it happens
Trigger: Calling any FS mutation endpoint (mkshortcut, copy, write, batchWrite, etc.) with no Authorization header / session cookie; a misconfigured route that drops the auth middleware; an internal call that bypasses the express pipeline and constructs a Request manually without attaching `actor`; token verification disabled in dev but the handler still requires an actor.
Common situations: Local dev with `requireVerified` or auth middleware disabled; a proxy stripping the Authorization header; using a service-to-service client that forgets to forward the user session; hitting the route during a session-expired race where the actor was cleared.
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/b4c33fbf8f67a195.
Report an issue: GitHub.