HeyPuter/puter · error · HttpError
not_found
not_found
Error message
Item not found
What it means
Thrown by #updateFSEntry when the fsentry lookup (by uid via getEntryByUuid, or by path via getEntryByPath) returns null. The item does not exist, was deleted, or the path/uid is malformed. It is a 404 not_found and fires before the ownership check, so it reveals no ownership information.
Source
Thrown at src/backend/controllers/desktop/DesktopController.js:237
* works for old accounts too).
*
* Ownership: `entry.user_id === actor.user.id`. Kept as added validation
* against bad paths — the previous "drop user_id entirely" theory turned
* out to be wrong (the actual legacy issue was NULL paths, not user_id
* drift), so this filter doesn't lock out old accounts in practice.
*/
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],View on GitHub (pinned to 908ec23eda)
Solutions
- Re-fetch the item's current uid/path (e.g. via puter.fs.stat / the fsentry listing) before retrying; a deleted item cannot be updated.
- Verify the path is absolute and rooted in the actor's own space.
- If this recurs for a known-existing item, check the recursive-CTE path fallback isn't disabled and the fsentries row's path/user_id columns aren't NULL.
Example fix
// before
{ item_uid: staleUid, layout: 'icons' }
// after
const entry = await puter.fs.stat(currentPath);
{ item_uid: entry.id, layout: 'icons' } Defensive patterns
Strategy: validation
Validate before calling
const entry = item_uid
? await fsEntry.getEntryByUuid(item_uid)
: await fsEntry.getEntryByPath(item_path);
if (!entry) throw new NotFoundError('Item not found'); Type guard
const exists = <T>(v: T | null | undefined): v is T => v != null;
Try / catch
try {
await setLayout(...);
} catch (e) {
if (isHttpError(e, 404)) { /* re-resolve uid/path or give up */ }
else throw e;
} Prevention
- Re-fetch the current uid before retrying after a delete/move.
- Confirm path is absolute and inside the user's space.
- Treat 404 as terminal for that identifier — don't loop.
When it happens
Trigger: POST /set_layout or /set_sort_by with an item_uid that was never created or since deleted, or an item_path that doesn't resolve (typo, wrong root, points into another user's space but doesn't exist).
Common situations: Stale uid cached in the GUI after the folder was deleted or moved; path constructed from a user-typed string with a typo; referencing an item from a different account by guessing a path.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/ac6e0fcbd0a9a58c.
Report an issue: GitHub.