anomalyco/sst · error
In "${name}" Router, the route path "${path}" can only have
Error message
In "${name}" Router, the route path "${path}" can only have one of url or bucket What it means
Each inline route must resolve to exactly one target kind: a `url` (string or object) or a `bucket`. normalizeRoutes counts the presence of these keys and throws when a route specifies neither or both.
Source
Thrown at platform/src/components/aws/router.ts:1756
const cdn = createCdn();
return cdn;
function normalizeRoutes() {
return output(args.routes!).apply((routes) => {
const normalizedRoutes = Object.fromEntries(
Object.entries(routes).map(([path, route]) => {
// Route path must start with "/"
if (!path.startsWith("/"))
throw new Error(
`In "${name}" Router, the route path "${path}" must start with a "/"`,
);
route = typeof route === "string" ? { url: route } : route;
const hasUrl = "url" in route ? 1 : 0;
const hasBucket = "bucket" in route ? 1 : 0;
if (hasUrl + hasBucket !== 1)
throw new Error(
`In "${name}" Router, the route path "${path}" can only have one of url or bucket`,
);
return [path, route];
}),
);
normalizedRoutes["/*"] = normalizedRoutes["/*"] ?? {
url: "https://do-not-exist.sst.dev",
};
return normalizedRoutes;
});
}
function createCfRequestDefaultFunction() {
defaultCfFunction =
defaultCfFunction ??View on GitHub (pinned to a0bd20f762)
Solutions
- Provide exactly one of `url` or `bucket` on each route entry
- Remove the extra key (e.g. delete the spread `...common` that injects a second target)
- If neither is intended, drop the route entirely or fix the key name
Example fix
// before
routes: { "/api/*": { url: "https://api.example.com", bucket: myBucket } }
// after
routes: { "/api/*": { url: "https://api.example.com" } } Defensive patterns
Strategy: validation
Validate before calling
for (const [path, route] of Object.entries(routes)) {
const r = typeof route === "string" ? { url: route } : route;
const count = ("url" in r ? 1 : 0) + ("bucket" in r ? 1 : 0);
if (count !== 1) throw new Error(`Route "${path}" must set exactly one of url or bucket`);
} Type guard
function hasOneTarget(r: object): boolean {
return ("url" in r ? 1 : 0) + ("bucket" in r ? 1 : 0) === 1;
} Try / catch
try {
const r = new Router(ctx, "R", { routes });
} catch (e) {
if (String(e).includes("one of url or bucket")) console.error("Route entry ambiguous");
throw e;
} Prevention
- Avoid spreading shared objects into route entries that may already carry a target
- Model route configs with a discriminated union type (url vs bucket)
- Validate generated route maps in unit tests before deploy
When it happens
Trigger: A route entry like `{ url: "...", bucket: "..." }`, `{ url: ..., bucket: ... }` inherited via spread, or `{}` (neither key) passed to the Router's `routes` map.
Common situations: Spreading a shared options object that already contains `bucket` into a route that also sets `url`; building route configs programmatically and leaving a route empty; typos like `buckets` that leave the route with zero recognized targets.
Related errors
- Cannot set "protection" on a Router with inline routes. Use
- In "${name}" Router, the route path "${path}" must start wit
- Invalid route type
- Cannot use both `routes` and `.route()` function to add rout
- Cannot use both `routes` and `.routeBucket()` function to ad
AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30).
Data as JSON: /api/errors/c9a32d8db7e9945e.
Report an issue: GitHub.