HeyPuter/puter · error · HttpError
forbidden
forbidden
Error message
Not allowed to update this item
What it means
Thrown by #updateFSEntry (403 forbidden) when the resolved fsentry's userId does not equal the actor's user id, or the actor has no valid numeric user id. This is the ownership gate: you can only set layout/sort on folders you own, even if the path resolves.
Source
Thrown at src/backend/controllers/desktop/DesktopController.js:244
async #updateFSEntry(actor, { item_uid, item_path }, patch) {
if (!item_uid && !item_path) {
throw new HttpError(400, 'Missing `item_uid` or `item_path`', {
legacyCode: 'bad_request',
});
}
const entry = item_uid
? await this.stores.fsEntry.getEntryByUuid(item_uid)
: await this.stores.fsEntry.getEntryByPath(item_path);
if (!entry) {
throw new HttpError(404, 'Item not found', {
legacyCode: 'not_found',
});
}
const actorUserId = actor?.user?.id;
if (typeof actorUserId !== 'number' || entry.userId !== actorUserId) {
throw new HttpError(403, 'Not allowed to update this item', {
legacyCode: 'forbidden',
});
}
const keys = Object.keys(patch);
const setClause = keys.map((k) => `\`${k}\` = ?`).join(', ');
const values = keys.map((k) => patch[k]);
await this.db.write(
`UPDATE \`fsentries\` SET ${setClause} WHERE \`id\` = ?`,
[...values, entry.id],
);
await this.stores.fsEntry.invalidateEntryCacheByUuid(entry.uuid);
}
onServerStart() {}
onServerPrepareShutdown() {}View on GitHub (pinned to 908ec23eda)
Solutions
- Only attempt preference writes on folders owned by the authenticated user; for shared folders, preferences belong to the owner, not the viewer.
- Ensure the access token is a full user-scoped token (requireUserActor + allowFullAccessToken are both on) for the owner.
- If the actor shape changed, confirm auth middleware still populates req.actor.user.id as a number.
Example fix
// no code fix — this is a contract violation; verify ownership first
const entry = await fsEntry.getEntryByPath(p);
if (entry.userId !== currentUser.id) throw new Error('not owner'); Defensive patterns
Strategy: validation
Validate before calling
const entry = await fsEntry.getEntryByPath(p);
if (!entry || entry.userId !== currentUser.id) {
throw new Error('not owner');
} Type guard
const isOwner = (entry: { userId: number }, user: { id: number }): boolean =>
entry.userId === user.id; Prevention
- Only write preferences on folders you own.
- Use a full user access token.
- Shared folders' preferences belong to the owner, not the viewer.
When it happens
Trigger: POST /set_layout or /set_sort_by referencing another user's folder by guessed uid/path; a shared/hosted item where the caller is not the owner; a token whose actor.user.id is missing or non-numeric (mis-issued or anonymous actor).
Common situations: Using a public-share path to try to write preferences on someone else's folder; an internal/service token missing the user scope; race where the folder was transferred to another user between read and write.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/d2a1ea2e1e631b3a.
Report an issue: GitHub.