HeyPuter/puter · error · HttpError

unauthorized

unauthorized

Error message

Unauthorized

What it means

Thrown by POST /app-feedback when the authenticated actor has no associated user id (req.actor?.user?.id is falsy). The route is gated by requireUserActor and requireVerified, so this fires only when those gates passed but the user object is nonetheless missing an id — an inconsistency in the auth pipeline rather than a normal unauthenticated request.

Source

Thrown at src/backend/controllers/feedback/AppFeedbackController.ts:153

                legacyCode: 'bad_request',
            });
        }
        if (message.length > RAW_MESSAGE_CAP) {
            throw new HttpError(
                400,
                `\`message\` is too long (max ${AppFeedbackService.MESSAGE_MAX_LENGTH} characters)`,
                { legacyCode: 'bad_request' },
            );
        }

        const sourceEnv =
            body.context === 'app' || body.context === 'web'
                ? body.context
                : undefined;

        const userId = req.actor?.user?.id;
        if (!userId) {
            throw new HttpError(401, 'Unauthorized', {
                legacyCode: 'unauthorized',
            });
        }

        const service = this.services.appFeedback as AppFeedbackService;
        await service.submit({
            userId,
            app,
            origin,
            message,
            sourceEnv,
            sourceOrigin: origin ?? null,
        });
        res.json({});
    }
}

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Verify the request carries a valid user session token (not an app or anonymous token).
  2. Check that the auth middleware populates req.actor.user.id for the token type in use.
  3. Re-authenticate to mint a fresh user token if the current one predates the running backend version.
Defensive patterns

Strategy: try-catch

Try / catch

try { await fetch('/app-feedback', { ... }); }
catch (e) {
  if (e?.code === 'unauthorized') { /* re-authenticate the user, then retry once */ await relogin(); return retry(); }
  throw e;
}

Prevention

When it happens

Trigger: A request reaches the handler with an actor that is not a user actor (e.g. a service/worker actor that bypassed requireUserActor), or a user actor whose user row failed to load. Also possible during auth refactor where the actor shape changed but a stale token type still satisfies the gate.

Common situations: Misconfigured auth middleware after a Puter upgrade that changed actor serialization. A token minted for a since-deleted user. Local dev with a hand-crafted session that skips normal user hydration.

Understand the failure class

Related errors


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