HeyPuter/puter · error · HttpError

not_found

not_found

Error message

App not found

What it means

Thrown by POST /sign when the body includes a non-empty `app_uid` (requesting an app grant + token alongside the signatures) but no app exists with that uid in the app store. The optional-grant path looks the app up and 404s when it cannot find it, since there is nothing to grant to.

Source

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

            if (!username || !appUid)
                throw new HttpError(403, 'Forbidden', {
                    legacyCode: 'forbidden',
                });
            appDataRoot = `/${username}/AppData/${appUid}`;
        }

        type SignedOrEmpty =
            (SignedFile & { path?: string }) | Record<string, never>;
        const result: { signatures: SignedOrEmpty[]; token?: string } = {
            signatures: [],
        };

        // Optional app grant: provide app_uid to grant permissions + token.
        let grantApp: { uid: string } | null = null;
        if (typeof body.app_uid === 'string' && body.app_uid.length > 0) {
            const app = await this.stores.app.getByUid(body.app_uid);
            if (!app)
                throw new HttpError(404, 'App not found', {
                    legacyCode: 'not_found',
                });
            grantApp = { uid: app.uid };
            result.token = await this.services.auth.getUserAppToken(
                actor,
                app.uid,
            );
        }

        for (const rawItem of items) {
            const item = asRecord(rawItem);
            const uid = typeof item.uid === 'string' ? item.uid : undefined;
            const path = typeof item.path === 'string' ? item.path : undefined;
            const action =
                typeof item.action === 'string' ? item.action : 'read';
            if (!uid && !path) {
                result.signatures.push({});
                continue;

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Verify the app_uid exists: list apps or check the app's dashboard/registry before sending.
  2. Drop body.app_uid if you do not need the app-grant + token (the signatures are returned without it).
  3. Regenerate the uid from the current environment's app store rather than reusing one from elsewhere.

Example fix

// before
await fetch('/sign', { method:'POST', body: JSON.stringify({ items, app_uid: 'old-uid' }) })
// after — either omit app_uid, or use a verified one
await fetch('/sign', { method:'POST', body: JSON.stringify({ items }) })
Defensive patterns

Strategy: validation

Validate before calling

// verify the app exists (or omit app_uid) before signing
async function resolveAppUid(appStore, appUid) {
  if (!appUid) return null;
  const app = await appStore.getByUid(appUid);
  if (!app) throw new Error(`app_uid ${appUid} not found; drop it or correct it`);
  return app.uid;
}

Try / catch

try { await fetch('/sign', { method:'POST', body: JSON.stringify({ items, app_uid }) }); }
catch (e) { if (e.code === 'not_found' && /app/i.test(e.message)) { appUid = undefined; /* retry without grant */ } else throw e; }

Prevention

When it happens

Trigger: POST /sign with body.app_uid pointing to a deleted app, a typo'd uid, or an app uid from a different environment (e.g. staging uid used against production).

Common situations: Hardcoded app_uid that was removed; copy-paste error; cross-environment config bleed; app pending approval not yet in the store.

Related errors


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