{"record":{"id":"6586178c42b62f1a","repo":"remix-run/react-router","slug":"path-path-requires-param-param-but-it-wa","errorCode":null,"errorMessage":"Path '${path}' requires param '${param}' but it was not provided","messagePattern":"Path '(.+?)' requires param '(.+?)' but it was not provided","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/href.ts","lineNumber":58,"sourceCode":" * @category Utils\n * @mode framework\n * @param path The route path to resolve\n * @param args The route params to use when resolving the path\n * @returns The resolved URL path\n */\nexport function href<Path extends keyof Args>(\n  path: Path,\n  ...args: Args[Path]\n): string {\n  let params = args[0];\n  let result = trimTrailingSplat(path) // Ignore trailing / and /*, we'll handle it below\n    .replace(\n      /\\/:([\\w-]+)(\\?)?/g, // same regex as in .\\router\\utils.ts: compilePath().\n      (_: string, param: string, questionMark: string | undefined) => {\n        const isRequired = questionMark === undefined;\n        const value = params?.[param];\n        if (isRequired && value === undefined) {\n          throw new Error(\n            `Path '${path}' requires param '${param}' but it was not provided`,\n          );\n        }\n        return value == null ? \"\" : \"/\" + encodePathParam(stringify(value));\n      },\n    );\n\n  if (path.endsWith(\"*\")) {\n    // treat trailing splat the same way as compilePath, and force it to be as if it were `/*`.\n    // `react-router typegen` will not generate the params for a malformed splat,\n    // causing a type error, but we can still do the correct thing here.\n    const value = params?.[\"*\"];\n    if (value !== undefined) {\n      result +=\n        \"/\" + stringify(value).split(\"/\").map(encodePathParam).join(\"/\");\n    }\n  }\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router/lib/href.ts#L40-L76","documentation":"href() interpolates dynamic segments at runtime; any required :param (one without a trailing ?) that has no value in the params argument throws (lib/href.ts:58). With generated route types TypeScript catches this at compile time, so a runtime hit usually means the path string bypassed the typed surface (template literals, casts, or missing typegen).","triggerScenarios":"href('/users/:id', {}) with no id; href(path as any, params) casting away the type; param name typo like href('/blog/:slug', { id: post.id }); passing undefined/null as the params object for a path with required segments.","commonSituations":"Building path strings dynamically at runtime; skipping `react-router typegen` after adding routes; renaming a route param in routes.ts but not at call sites; copy-pasting href() calls from untyped examples.","solutions":["Supply a value for every required :param in the path","Run `react-router typegen` and keep path strings typed so the compiler catches missing params","If the segment is genuinely optional, declare it as :param? in the route path","Check for typos between the param name in the path string and the keys of the params object"],"exampleFix":"// before\nhref('/blog/:slug', {})\n// after\nhref('/blog/:slug', { slug: post.slug })","handlingStrategy":"validation","validationCode":"function assertParams(path: string, params: Record<string, string | undefined>) {\n  const required = [...path.matchAll(/\\/:([\\w-]+)(?!\\?)/g)].map((m) => m[1]);\n  const missing = required.filter((p) => params[p] == null);\n  if (missing.length) throw new Error(`Missing params: ${missing.join(', ')}`);\n}\nassertParams('/users/:id/posts/:postId', params);","typeGuard":"function hasRequiredParams(path: string, params: unknown): params is Record<string, string> {\n  const required = [...path.matchAll(/\\/:([\\w-]+)(?!\\?)/g)].map((m) => m[1]);\n  return required.every((p) => typeof (params as any)?.[p] === 'string' && (params as any)[p] !== '');\n}","tryCatchPattern":"try { return href(path, params) } catch (e) { if (e instanceof Error && e.message.includes('requires param')) { return fallbackPath } throw e }","preventionTips":["Run `react-router typegen` in CI so untyped href calls fail the build","Avoid `as any` on path strings; keep them literal and typed","Unit-test dynamic href builders with all params defined"],"tags":["href","params","routing","typescript","typegen"],"backgroundTag":"missing-route-param","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}