HeyPuter/puter · error · HttpError
forbidden
forbidden
Error message
Prompt blocked by policy
What it means
Thrown (HTTP 403, legacy code 'forbidden') when an ai.prompt.validate listener flagged the prompt as disallowed (allow=false, e.g. content moderation/abuse) but no fake-chat model ('fake' or 'abuse') is registered in config. Normally a blocked prompt is silently rerouted to a fake-chat model that returns lorem-ipsum (or a phone-home script for abuse); this throw is the fallback when that reroute target is itself missing. It signals a server-side configuration gap, not a client content violation.
Source
Thrown at src/backend/drivers/ai-chat/ChatCompletionDriver.ts:367
};
await this.clients.event.emitAndWait(
'ai.prompt.validate',
validateEvent,
{},
);
// Blocked prompts get rerouted to fake-chat. With `event.abuse` we
// pick the `abuse` model, which embeds `event.custom` (phone-home
// script for bots, etc.) in its response so the bot's renderer
// executes it. Without `abuse`, we silently route to the default
// `fake` model (lorem-ipsum response). Mirrors v1 AIChatService.
let blocked = false;
if (!validateEvent.allow) {
const fakeModelId = validateEvent.abuse ? 'abuse' : 'fake';
const fakeModel = this.#resolveModel(fakeModelId, 'fake-chat');
if (!fakeModel) {
throw new HttpError(403, 'Prompt blocked by policy', {
legacyCode: 'forbidden',
});
}
blocked = true;
model = fakeModel;
intendedProvider = 'fake-chat';
if (typeof validateEvent.custom !== 'undefined') {
args.custom = validateEvent.custom;
}
}
// -- Credit / subscription gates (metering) --------------------
// Cheap pre-flight: reject when the user can't afford even the
// approximate input cost, keep subscriber-only models gated, and
// cap `max_tokens` so output can't exceed remaining credits.
// Skipped for blocked requests since fake-chat is free and the
// user shouldn't see a billing error in place of the abuse page.
if (!blocked) {View on GitHub (pinned to 908ec23eda)
Solutions
- Register a 'fake' (and optionally 'abuse') model in the fake-chat provider config so #resolveModel succeeds.
- If moderation is not wanted, disable or remove the listener that sets event.allow=false on ai.prompt.validate.
- Verify the model registry entry the driver resolves with the 'fake-chat' intended provider name.
- As an end user, rephrase the prompt to avoid the moderation trigger, or contact the instance operator about the missing fake-chat model.
Example fix
// config.json models array — add a fake-chat entry
// before: fake-chat provider has no models registered
// after:
{
"provider": "fake-chat",
"id": "fake",
"intended_provider": "fake-chat"
} Defensive patterns
Strategy: try-catch
Try / catch
try {
const res = await driver.complete(args);
} catch (e) {
if (e instanceof HttpError && e.status === 403 && e.body?.legacyCode === 'forbidden') {
// prompt tripped moderation and the instance has no fake-chat model;
// surface a user-facing 'content not allowed' message rather than retry
return showModerationNotice();
}
throw e;
} Prevention
- Operators: always register 'fake' (and 'abuse' if moderation phones home) models when a prompt.validate listener is enabled.
- Do not retry the same prompt — the moderation result is deterministic for the same input.
- Surface the error as a content-policy notice to the end user; it is not a transient failure.
When it happens
Trigger: An event handler attached to 'ai.prompt.validate' sets event.allow=false on a request, and the model registry has no entry resolvable by ChatCompletionDriver.#resolveModel(fakeModelId, 'fake-chat') for either the 'fake' or 'abuse' id. Triggered by any chat completion whose prompt matches the moderation/abuse predicate.
Common situations: Self-hosted Puter installs where the fake-chat models were stripped from config.json but a moderation extension is still enabled; or a deploy that registered an abuse-detection listener without shipping the companion fake-chat models. End users see it only if their prompt tripped moderation on such a misconfigured server.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/9db4dd747a4f13b2.
Report an issue: GitHub.