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

  1. 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.
  2. Verify the path is absolute and rooted in the actor's own space.
  3. 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

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.