anomalyco/sst · error · VisibleError
Cannot set "protection" on a Router with inline routes. Use
Error message
Cannot set "protection" on a Router with inline routes. Use lazy routes instead.
What it means
Router 'protection' (WAF-based CAPTCHA/challenge settings) only works with the lazy `.route()`/`.routeBucket()` API because protection is applied per-route at the edge. If you pass inline `routes` and also set protection with mode other than "none", the constructor throws to prevent an invalid configuration.
Source
Thrown at platform/src/components/aws/router.ts:1404
if (args && "ref" in args) {
const ref = reference();
this.cdn = output(ref.cdn);
this.kvStoreArn = ref.kvStoreArn;
this.kvNamespace = ref.kvNamespace;
this.hasInlineRoutes = ref.hasInlineRoutes;
this._protectionMode = ref.protection;
registerOutputs();
return;
}
const hasInlineRoutes = args.routes !== undefined;
const protection = normalizeProtection();
if (hasInlineRoutes) {
protection.apply((p) => {
if (p.mode !== "none")
throw new VisibleError(
`Cannot set "protection" on a Router with inline routes. Use lazy routes instead.`,
);
});
}
const waf = createWaf();
const wafArn = waf?.arn;
const wafLogging = normalizeWafLogging();
createWafLogging();
let cdn, kvStoreArn, kvNamespace;
if (hasInlineRoutes) {
cdn = handleInlineRoutes();
} else {
const r = handleLazyRoutes();
cdn = output(r.distribution);
kvStoreArn = r.kvStoreArn;
kvNamespace = output(r.kvNamespace);View on GitHub (pinned to a0bd20f762)
Solutions
- Remove the inline `routes` object and define routes lazily with `.route()` / `.routeBucket()` so protection can be applied
- Set `protection: { mode: "none" }` if you genuinely want inline routes without protection
- Move the WAF/protection config onto the individual lazy route definitions
Example fix
// before
const router = new Router($app, "Router", {
routes: { "/*": { url: "https://example.com" } },
protection: { mode: "captcha" },
});
// after
const router = new Router($app, "Router", {});
router.route("/*", "https://example.com", {
protection: { mode: "captcha" },
}); Defensive patterns
Strategy: validation
Validate before calling
const routes = { "/*": { url: "https://example.com" } };
const protection = { mode: "captcha" } as const | undefined;
if (routes && protection && protection.mode !== "none")
throw new Error("Cannot combine inline routes with protection; use lazy routes"); Type guard
function usesInlineRoutesWithProtection(args: { routes?: unknown; protection?: { mode: string } }): boolean {
return args.routes !== undefined && !!args.protection && args.protection.mode !== "none";
} Try / catch
try {
router = new Router(ctx, "Router", args);
} catch (e) {
if (String(e).includes('Cannot set "protection"')) {
// rebuild args without inline routes
}
throw e;
} Prevention
- Decide up front: inline routes OR protection, never both
- When WAF protection is needed, always use the lazy .route()/.routeBucket() API
- Keep a lint rule/helper that validates Router args in one place
When it happens
Trigger: `new Router(..., { routes: {...}, protection: { mode: "captcha" | "challenge" | ... } })` — inline routes combined with any non-none protection mode.
Common situations: Migrating an existing inline-routes Router to use WAF protection; copy-pasting protection args from a lazy-route example into an inline-routes config.
Related errors
- In "${name}" Router, the route path "${path}" can only have
- Cannot use both `routes` and `.route()` function to add rout
- Cannot use both `routes` and `.routeBucket()` function to ad
- There have been some minor changes to the "Router" component
- In "${name}" Router, the route path "${path}" must start wit
AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30).
Data as JSON: /api/errors/c68cc4b0804c8cf6.
Report an issue: GitHub.