{"record":{"id":"635fcff4a6671cc2","repo":"clockworklabs/SpacetimeDB","slug":"route-paths-must-start-with-path","errorCode":null,"errorMessage":"Route paths must start with `/`: ${path}","messagePattern":"Route paths must start with `/`: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/http_handlers.ts","lineNumber":104,"sourceCode":"    return body;\n  }\n  return textDecoder.decode(body);\n}\n\nfunction characterIsAcceptableForRoutePath(c: string) {\n  return (\n    (c >= 'a' && c <= 'z') ||\n    (c >= '0' && c <= '9') ||\n    c === '-' ||\n    c === '_' ||\n    c === '~' ||\n    c === '/'\n  );\n}\n\nfunction assertValidPath(path: string) {\n  if (path !== '' && !path.startsWith('/')) {\n    throw new TypeError(`Route paths must start with \\`/\\`: ${path}`);\n  }\n  if (![...path].every(characterIsAcceptableForRoutePath)) {\n    throw new TypeError(\n      `Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION}: ${path}`\n    );\n  }\n}\n\nfunction routesOverlap(a: RouteSpec, b: RouteSpec) {\n  const methodsMatch = (left: HttpMethod, right: HttpMethod) => {\n    if (left.tag !== right.tag) {\n      return false;\n    }\n    if (left.tag === 'Extension' && right.tag === 'Extension') {\n      return left.value === right.value;\n    }\n    return true;\n  };","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-typescript/src/server/http_handlers.ts#L86-L122","documentation":"Server modules register HTTP routes through Router (router.get/post/put/.../any and router.nest). assertValidPath requires every route path to be either '' (the module root) or begin with '/', since paths are matched literally by the HTTP dispatch layer. A path missing the leading slash is a programmer error and throws a TypeError at module build time.","triggerScenarios":"Registering a route with a relative path such as router.get('health', handler) or router.any('api/users', handler); '' is the only permitted value that does not start with '/'.","commonSituations":"Porting handlers from Express-style code where 'users' is tolerated; building paths dynamically (base + '/users') and forgetting the slash on the first segment; simple typos.","solutions":["Prepend '/' to the offending path: 'health' becomes '/health'","If the path is computed, normalize it: const p = raw === '' || raw.startsWith('/') ? raw : '/' + raw","Run module tests so route registration executes before deploy; this throws during build, never at request time"],"exampleFix":"// before\nrouter.get('health', handler);\n\n// after\nrouter.get('/health', handler);","handlingStrategy":"validation","validationCode":"function assertRoutePathStart(path: string): void {\n  if (path !== '' && !path.startsWith('/')) {\n    throw new TypeError(`Route paths must start with \\`/\\`: ${path}`);\n  }\n}\n// call on every path before router.get/post/...","typeGuard":"const hasLeadingSlash = (p: string): p is `/${string}` | '' =>\n  p === '' || p.startsWith('/');","tryCatchPattern":null,"preventionTips":["Centralize route path constants in one file and write them with leading slashes","Add a unit test that registers every route so build-time TypeErrors fail CI, not deploy","When composing paths dynamically, normalize with a helper that prepends '/' when missing"],"tags":["http","router","validation","typescript"],"backgroundTag":"invalid-route-path","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}