HeyPuter/puter · error · HttpError
not_found
not_found
Error message
App not found
What it means
Returned by POST /rao after the authorization checks pass but appStore.getByUid(app_uid) resolves to null — the app uid supplied (from the body or the actor's bound app) does not correspond to any stored app. It indicates the credential is valid but points at a nonexistent app record.
Source
Thrown at src/backend/controllers/apps/AppController.js:243
if (isAccessTokenActor(actor)) {
throw new HttpError(
403,
'Access tokens cannot report app opens',
{ legacyCode: 'forbidden' },
);
}
if (isAppActor(actor) && app_uid !== actorAppUid) {
throw new HttpError(
403,
'App actors can only report opens for their own app',
{ legacyCode: 'forbidden' },
);
}
const app = await this.appStore.getByUid(app_uid);
if (!app)
throw new HttpError(404, 'App not found', {
legacyCode: 'not_found',
});
// Validation and authorization are settled by this point, so
// the caller learns the outcome now and the stats write lands
// on its own. See `#recordAppOpen`.
this.#recordAppOpen(app_uid, req.actor.user.id);
res.json({});
},
);
// GET /apps/:name — returns the app(s) by name.
// Supports pipe-separated names for batch lookup: /apps/foo|bar|baz
router.get(
'/apps/:name',
{
subdomain: 'api',View on GitHub (pinned to 908ec23eda)
Solutions
- Verify the app_uid exists (GET /apps/:name or a lookup) before reporting opens.
- If using an app actor, ensure the token was minted for a currently-registered app; re-issue the token if the app was re-created with a new uid.
- Check for typos or copy errors in the app_uid value passed in the body.
Example fix
// before
await fetch('/rao', { method:'POST', body:JSON.stringify({app_uid: 'app-does-not-exist'}) });
// after: confirm the uid resolves first
const app = await (await fetch(`/apps/${name}`)).json();
await fetch('/rao', { method:'POST', body:JSON.stringify({app_uid: app.uid}) }); Defensive patterns
Strategy: validation
Validate before calling
const exists = await appStore.getByUid(app_uid);
if (!exists) throw new Error('Refusing to report open for unknown app'); Type guard
/** @returns {boolean} */
function isValidAppUid(uid) {
return typeof uid === 'string' && uid.length > 0 && uid.startsWith('app-');
} Try / catch
try { await postRao(app_uid); }
catch (e) { if (e.code === 'not_found') { /* re-resolve uid or drop the report */ } else throw e; } Prevention
- Resolve the app uid from a trusted source (the app record) rather than hardcoding.
- Treat a 404 as non-fatal for analytics — drop the report rather than retrying forever.
When it happens
Trigger: Posting an app_uid that was deleted, never existed, or is malformed-but-nonempty; using an app token whose bound app.uid was removed from the store after the token was minted.
Common situations: Stale token from a deleted app; typo in a hardcoded app_uid; test environment where the app row was never seeded; race where the app is removed between token issuance and the open report.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/4abb997e34c4d247.
Report an issue: GitHub.