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

  1. Remove the inline `routes` object and define routes lazily with `.route()` / `.routeBucket()` so protection can be applied
  2. Set `protection: { mode: "none" }` if you genuinely want inline routes without protection
  3. 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

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


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/c68cc4b0804c8cf6. Report an issue: GitHub.