HeyPuter/puter · error · HttpError

subject_does_not_exist

subject_does_not_exist

Error message

Entry not found: uid=${uid}

What it means

Thrown (HTTP 404) by LegacyFSController.updateFsentryThumbnail when no fs entry exists for the supplied uid, or the entry has no path. The lookup uses stores.fsEntry.getEntryByUuid(uid); a missing/null entry or one without a path is reported as subject_does_not_exist.

Source

Thrown at src/backend/controllers/fs/LegacyFSController.ts:1075

            throw new HttpError(400, 'Missing `uid`', {
                legacyCode: 'bad_request',
            });
        if (!thumbnail)
            throw new HttpError(400, 'Missing `thumbnail`', {
                legacyCode: 'bad_request',
            });
        // Only inline image data. Clients generate the thumbnail themselves
        // and the thumbnails extension is what turns it into a storage
        // pointer; accepting a pointer here would let a caller name an object
        // the server would then sign reads of, and delete, on their behalf.
        if (!thumbnail.startsWith('data:'))
            throw new HttpError(400, '`thumbnail` must be a data: URL', {
                legacyCode: 'bad_request',
            });

        const entry = await this.stores.fsEntry.getEntryByUuid(uid);
        if (!entry || !entry.path)
            throw new HttpError(404, `Entry not found: uid=${uid}`, {
                legacyCode: 'subject_does_not_exist',
            });
        await assertAccess(
            this.services.acl,
            this.services.fs,
            actor,
            entry.path,
            'write',
        );

        // emitAndWait is required: the thumbnails extension rewrites
        // `event.url` from a data URL to an `s3://` pointer, and the DB
        // write below needs to see that rewrite.
        const event = { url: thumbnail };
        await this.clients.event.emitAndWait('thumbnail.created', event, {});

        await this.clients.db.write(
            'UPDATE `fsentries` SET `thumbnail` = ? WHERE `uuid` = ?',

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Re-fetch the entry to confirm it exists and obtain its current uuid before calling.
  2. Ensure you pass the entry's uuid (string) field, not its numeric id or name.
  3. Handle 404 gracefully in the UI (e.g. offer to retry or discard the stale reference).

Example fix

// before
updateFsentryThumbnail({ uid: 'maybe-old-id', thumbnail: dataUrl });

// after
const entry = await getEntry(path);
updateFsentryThumbnail({ uid: entry.uuid, thumbnail: dataUrl });
Defensive patterns

Strategy: try-catch

Validate before calling

const entry = await store.getEntryByUuid(uid);
if (!entry || !entry.path) throw new Error('Entry not found for uid');

Type guard

null

Try / catch

try {
  await updateFsentryThumbnail({ uid, thumbnail });
} catch (e) {
  if (e.code === 'subject_does_not_exist' || e.status === 404) {
    // refresh or discard the stale uid reference
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a uid that was never created, was deleted, belongs to another user (when lookup is scoped), or a uid typo. Also an entry row that exists but has a null path.

Common situations: Using a stale uid cached after the file was deleted; passing the entry's id (numeric DB id) instead of its uuid; uid copy/paste errors.

Related errors


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