{"record":{"id":"c9a32d8db7e9945e","repo":"anomalyco/sst","slug":"in-name-router-the-route-path-path-can","errorCode":null,"errorMessage":"In \"${name}\" Router, the route path \"${path}\" can only have one of url or bucket","messagePattern":"In \"(.+?)\" Router, the route path \"(.+?)\" can only have one of url or bucket","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/src/components/aws/router.ts","lineNumber":1756,"sourceCode":"      const cdn = createCdn();\n      return cdn;\n\n      function normalizeRoutes() {\n        return output(args.routes!).apply((routes) => {\n          const normalizedRoutes = Object.fromEntries(\n            Object.entries(routes).map(([path, route]) => {\n              // Route path must start with \"/\"\n              if (!path.startsWith(\"/\"))\n                throw new Error(\n                  `In \"${name}\" Router, the route path \"${path}\" must start with a \"/\"`,\n                );\n\n              route = typeof route === \"string\" ? { url: route } : route;\n\n              const hasUrl = \"url\" in route ? 1 : 0;\n              const hasBucket = \"bucket\" in route ? 1 : 0;\n              if (hasUrl + hasBucket !== 1)\n                throw new Error(\n                  `In \"${name}\" Router, the route path \"${path}\" can only have one of url or bucket`,\n                );\n\n              return [path, route];\n            }),\n          );\n\n          normalizedRoutes[\"/*\"] = normalizedRoutes[\"/*\"] ?? {\n            url: \"https://do-not-exist.sst.dev\",\n          };\n\n          return normalizedRoutes;\n        });\n      }\n\n      function createCfRequestDefaultFunction() {\n        defaultCfFunction =\n          defaultCfFunction ??","sourceCodeStart":1738,"sourceCodeEnd":1774,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/platform/src/components/aws/router.ts#L1738-L1774","documentation":"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.","triggerScenarios":"A route entry like `{ url: \"...\", bucket: \"...\" }`, `{ url: ..., bucket: ... }` inherited via spread, or `{}` (neither key) passed to the Router's `routes` map.","commonSituations":"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.","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"],"exampleFix":"// before\nroutes: { \"/api/*\": { url: \"https://api.example.com\", bucket: myBucket } }\n// after\nroutes: { \"/api/*\": { url: \"https://api.example.com\" } }","handlingStrategy":"validation","validationCode":"for (const [path, route] of Object.entries(routes)) {\n  const r = typeof route === \"string\" ? { url: route } : route;\n  const count = (\"url\" in r ? 1 : 0) + (\"bucket\" in r ? 1 : 0);\n  if (count !== 1) throw new Error(`Route \"${path}\" must set exactly one of url or bucket`);\n}","typeGuard":"function hasOneTarget(r: object): boolean {\n  return (\"url\" in r ? 1 : 0) + (\"bucket\" in r ? 1 : 0) === 1;\n}","tryCatchPattern":"try {\n  const r = new Router(ctx, \"R\", { routes });\n} catch (e) {\n  if (String(e).includes(\"one of url or bucket\")) console.error(\"Route entry ambiguous\");\n  throw e;\n}","preventionTips":["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"],"tags":["router","route-validation","config-validation","sst"],"backgroundTag":"invalid-route-path","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}