{"record":{"id":"8d7b2a0349fc8827","repo":"remix-run/react-router","slug":"the-serverbundles-function-must-return-a-string","errorCode":null,"errorMessage":"The \"serverBundles\" function must return a string","messagePattern":"The \"serverBundles\" function must return a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugin.ts","lineNumber":3465,"sourceCode":"    serverBundles: {},\n    routeIdToServerBundleId: {},\n    routes: rootRelativeRoutes,\n  };\n\n  await Promise.all(\n    getAddressableRoutes(routes).map(async (route) => {\n      let branch = getRouteBranch(routes, route.id);\n      let serverBundleId = await serverBundles({\n        branch: branch.map((route) =>\n          configRouteToBranchRoute({\n            ...route,\n            // Ensure absolute paths are passed to the serverBundles function\n            file: path.join(resolvedAppDirectory, route.file),\n          }),\n        ),\n      });\n      if (typeof serverBundleId !== \"string\") {\n        throw new Error(`The \"serverBundles\" function must return a string`);\n      }\n      // Server bundle IDs must be valid Vite environment names, so hyphens are not allowed\n      if (!/^[a-zA-Z0-9_]+$/.test(serverBundleId)) {\n        throw new Error(\n          `The \"serverBundles\" function must only return strings containing alphanumeric characters and underscores.`,\n        );\n      }\n      buildManifest.routeIdToServerBundleId[route.id] = serverBundleId;\n\n      buildManifest.serverBundles[serverBundleId] ??= {\n        id: serverBundleId,\n        file: normalizePath(\n          path.join(\n            path.relative(\n              rootDirectory,\n              path.join(serverBuildDirectory, serverBundleId),\n            ),\n            reactRouterConfig.serverBuildFile,","sourceCodeStart":3447,"sourceCodeEnd":3483,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/plugin.ts#L3447-L3483","documentation":"The `serverBundles` config lets you assign routes to server bundles via a callback that receives each route branch and must return a bundle ID string. This error fires when the callback returns something else (commonly `undefined` when a code path forgets a return). Bundle IDs drive output directories and Vite environment names, so a non-string is fatal during build-manifest generation.","triggerScenarios":"`serverBundles: ({ branch }) => { if (condition) return 'a'; }` where the fall-through returns undefined; returning a promise of non-string; returning null for branches you meant to leave in the default bundle.","commonSituations":"Writing a branching function that forgets a default return; refactoring bundle assignment and dropping the final `return`; assuming unlisted routes fall back automatically.","solutions":["Make `serverBundles` return a string on every code path (end with a default `return 'index'` or similar)","Type the callback so TS flags missing returns: `({ branch }: { branch: ServerBundlesFunctionArgs['branch'] }) => string | Promise<string>`","Use the route branch's ids/files to decide, and log the branch when unsure which path returned undefined"],"exampleFix":"// before (react-router.config.ts)\nserverBundles: ({ branch }) => {\n  if (branch.some((r) => r.id === 'routes/admin')) return 'admin';\n},\n\n// after\nserverBundles: async ({ branch }) => {\n  if (branch.some((r) => r.id === 'routes/admin')) return 'admin';\n  return 'index';\n},","handlingStrategy":"type-guard","validationCode":"// Type the callback so TypeScript rejects missing returns\nimport type { ServerBundlesFunction } from '@react-router/dev/config';\nconst serverBundles: ServerBundlesFunction = async ({ branch }) => {\n  return branch.some((r) => r.id?.startsWith('routes/admin')) ? 'admin' : 'index';\n};\n// enable tsconfig option \"noImplicitReturns\": true to catch fall-through paths","typeGuard":"const isBundleIdString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try {\n  await build();\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must return a string')) {\n    // add a default return to every branch of the serverBundles function\n  }\n}","preventionTips":["Always end the `serverBundles` function with a default `return 'index'`-style fallback","Turn on `noImplicitReturns` in tsconfig.json","Cover the function with a unit test iterating representative branches"],"tags":["server-bundles","config","build","callback"],"backgroundTag":"config-callback-invalid-return","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"}