HeyPuter/puter · warning · HttpError
forbidden
forbidden
Error message
Forbidden
What it means
Thrown after a valid wisp token when an extension listener on the `wisp.get-policy` event set `event.allow` falsy. The token was fine; an installed extension's policy logic denied the relay. The deny happens via `emitAndWait`, so async listeners that fetch policy data fully resolve before this check.
Source
Thrown at src/backend/controllers/wisp/WispController.ts:150
const isGuest = Boolean(decoded.guest);
let user: Record<string, unknown> | null = null;
if (!isGuest && decoded.user_uid) {
user = await this.stores.user.getByUuid(String(decoded.user_uid));
}
const event: Record<string, unknown> = {
allow: true,
policy: { allow: true },
guest: isGuest,
user,
};
// emitAndWait so async listeners can fetch policy data before
// mutating `event.allow` / `event.policy`; plain emit would return
// control before any awaited work completed.
await this.clients.event.emitAndWait('wisp.get-policy', event, {});
if (!event.allow) {
throw new HttpError(403, 'Forbidden', { legacyCode: 'forbidden' });
}
res.json(event.policy);
};
#wispConfig(): NonNullable<typeof this.config.wisp> {
return this.config.wisp ?? {};
}
}
View on GitHub (pinned to 908ec23eda)
Solutions
- Inspect every listener registered on 'wisp.get-policy' and the config they read.
- Confirm the calling user/guest is permitted by the active policy.
- If the deny is unintended, fix or disable the offending extension.
- Check extension logs for the policy decision rationale.
Defensive patterns
Strategy: try-catch
Try / catch
try {
policy = await verifyWispPolicy(token);
} catch (e) {
if (e.code === 'forbidden') {
// token was valid but an extension policy denied access — surface to user/admin
showPolicyDenied(e);
} else throw e;
} Prevention
- Document which 'wisp.get-policy' extensions are installed and their allow/deny rules.
- Log the policy decision in the extension so denials are diagnosable.
- Test policy extensions against guest vs. authenticated users before deploy.
When it happens
Trigger: A backend extension registered with `extension.on('wisp.get-policy', ...)` mutates `event.allow` (or `event.policy.allow`) to false based on the guest/user/policy payload — e.g. guest access disabled, or the resolved user not permitted by an org policy.
Common situations: Self-hosted instance with a custom policy/allowlist extension; admin disabled guest wisp relay; an enterprise policy extension blocks certain users; a buggy listener accidentally setting allow=false.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/32a4ed64cf9fc2b9.
Report an issue: GitHub.