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

  1. Verify the app_uid exists (GET /apps/:name or a lookup) before reporting opens.
  2. 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.
  3. 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

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.