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
- Verify the app_uid exists: list apps or check the app's dashboard/registry before sending.
- Drop body.app_uid if you do not need the app-grant + token (the signatures are returned without it).
- 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
- Only pass app_uid when you actually need the grant+token.
- Source app_uid from the live app store, not a hardcoded constant.
- Treat a 404 here as recoverable: retry without app_uid.
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
- not_found
- not_found
- subject_does_not_exist
- not_found
- ${key} must be an integer between ${min} and ${max}
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/eb7797bfa92809c5.
Report an issue: GitHub.