HeyPuter/puter · error · HttpError

not_found

not_found

Error message

Entry not found

What it means

Thrown by FSController.#resolveEntryForRequest when `resolveNode(..., { required: true })` returns null/undefined for a `parent` or `target` reference. The controller builds a ref from `path`, `uid`/`uuid`, or `id`, asks the fsEntry store to resolve it, and 404s if nothing matched. This guards every operation that derefs a node ref (mkshortcut parent/target, copy source/dest, etc.).

Source

Thrown at src/backend/controllers/fs/FSController.ts:1643

                rawPath !== undefined
                    ? mod.expandTildePath(rawPath, username)
                    : undefined,
            uid:
                typeof source.uid === 'string'
                    ? source.uid
                    : typeof source.uuid === 'string'
                      ? source.uuid
                      : undefined,
            id:
                typeof source.id === 'number' || typeof source.id === 'string'
                    ? source.id
                    : undefined,
        };
        const entry = await mod.resolveNode(this.stores.fsEntry, ref, {
            required: true,
        });
        if (!entry) {
            throw new HttpError(404, 'Entry not found', {
                legacyCode: 'not_found',
            });
        }
        return entry;
    }

    /**
     * Authorize creation of a new entry at `targetPath`. The standard rule is
     * write on the parent, but we also accept write on the target itself — this
     * lets an app create its own `/<user>/AppData/<app_uid>` folder (parent
     * `AppData` is off-limits, but the target is the app's own subtree per
     * ACLService's short-circuit) and lets recipients of a direct share on a
     * not-yet-existent path materialize it.
     */
    async #assertCanCreate(actor: Actor, targetPath: string) {
        const parent = pathPosix.dirname(targetPath);
        const parentForCheck = parent === '/' ? targetPath : parent;
        const fsService = this.services.fs;

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Re-resolve the ref against the source of truth (stat/readdir) and resend with the corrected path/uid/id.
  2. Prefer uid/uuid over path if the entry may have been renamed between calls.
  3. If the entry should be shared with the actor, confirm the share record exists and grants at least `see`.
  4. Handle 404 gracefully in the client and refresh the file listing rather than retrying blindly.

Example fix

// before
{ target: { uid: staleUid } }
// after
const fresh = await puter.fs.stat(targetPath);
{ target: { uid: fresh.uid } }
Defensive patterns

Strategy: validation

Validate before calling

async function resolveEntry(ref) {
  const entry = await puter.fs.stat(ref.path ?? ref.uid ?? ref.id);
  if (!entry) throw new Error(`Entry not found: ${JSON.stringify(ref)}`);
  return entry;
}

Type guard

/** @param {unknown} r */
function isNodeRef(r) {
  return !!r && typeof r === 'object' && ('path' in r || 'uid' in r || 'uuid' in r || 'id' in r);
}

Try / catch

try { await op(); }
catch (e) {
  if (e?.status === 404) { /* refresh listing, mark node stale */ }
  else throw e;
}

Prevention

When it happens

Trigger: POST /mkshortcut with a `parent`/`target` whose path doesn't exist, was deleted, belongs to another user without share, or whose uid/uuid/id is wrong. Passing a uid that was rotated or an id from a different shard. A race where the entry was deleted between listing and the mutate call.

Common situations: Stale cached uid/uuid on the client after a move/rename; using a path under another user's home without a share grant; copy-pasting a fragment of a uid; referring to an entry by `id` when the column type changed.

Related errors


AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12). Data as JSON: /api/errors/ad8d4f2e204c1c0f. Report an issue: GitHub.