{"record":{"id":"e9e2652b16b510f5","repo":"remix-run/remix","slug":"file-outside-mounts","errorCode":"FILE_OUTSIDE_MOUNTS","errorMessage":"File ${identityPath} is outside all configured mounts.","messagePattern":"File (.+?) is outside all configured mounts\\.","errorType":"error_code","errorClass":"AssetServerCompilationError","httpStatus":null,"severity":"error","filePath":"packages/assets/src/lib/files/compiler.ts","lineNumber":571,"sourceCode":"    })\n  }\n\n  if (!args.isAllowed(identityPath)) {\n    throw createAssetServerCompilationError(\n      `File \"${identityPath}\" is not allowed by the asset server access configuration. ` +\n        `Add a matching allowFiles or allowPackages rule, or remove a conflicting denyFiles rule.`,\n      {\n        code: 'FILE_NOT_ALLOWED',\n      },\n    )\n  }\n\n  let stableUrlPathname = args.routes.toUrlPathname(identityPath)\n  if (!stableUrlPathname) {\n    throw createAssetServerCompilationError(\n      `File ${identityPath} is outside all configured mounts.`,\n      {\n        code: 'FILE_OUTSIDE_MOUNTS',\n      },\n    )\n  }\n\n  return {\n    identityPath,\n    stableUrlPathname,\n  }\n}\n\nexport function createResponseForFile(\n  result: FileCompileResult,\n  options: {\n    cacheControl: string\n    ifNoneMatch: string | null\n    method: string\n  },\n): Response {","sourceCodeStart":553,"sourceCodeEnd":589,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/assets/src/lib/files/compiler.ts#L553-L589","documentation":"After a file passes the access check, the compiler must map its identity path to a servable URL via `args.routes.toUrlPathname(identityPath)`. A null/undefined return means the file does not live under any configured mount, so there is no URL namespace it can be served from, and compilation fails with FILE_OUTSIDE_MOUNTS.","triggerScenarios":"A file passes allowFiles/allowPackages validation but its directory is not covered by any mount configured on the file routes (e.g. allowed via a broad glob but the mount only covers a subdirectory, or the file resolves through a symlink outside all mount roots).","commonSituations":"Adding an allowFiles rule broader than the mounts (allowing files the mounts can't serve); moving assets to a new directory without adding a mount; symlinked dependencies or workspace packages whose real path falls outside every configured mount root.","solutions":["Add or extend a mount so the file's directory is covered, e.g. mount the assets directory in the file routes config","Align overly-broad allowFiles globs with the actual mount roots so allowed files are always servable","If the file comes from a linked/workspace package, mount that package's directory as well"],"exampleFix":"// before\nroutes: createFileRoutes({ mounts: { '/assets': 'app/assets' } })\n// file lives in app/private/logo.svg → outside all mounts\n\n// after\nroutes: createFileRoutes({ mounts: { '/assets': 'app/assets', '/private': 'app/private' } })","handlingStrategy":"validation","validationCode":"// Ensure each identity path falls under a mount root before compiling:\nfor (const file of filesToServe) {\n  if (!mounts.some(([, root]) => isWithin(root, file))) {\n    throw new Error(`configure a mount covering ${file}`)\n  }\n}","typeGuard":"function isWithin(root: string, path: string): boolean {\n  const rel = path.slice(root.length)\n  return path.startsWith(root) && (rel === '' || rel.startsWith('/'))\n}","tryCatchPattern":"try {\n  const resolved = compiler.resolvedFile(file)\n} catch (error) {\n  if (error?.code === 'FILE_OUTSIDE_MOUNTS') {\n    // add a mount for the file's directory, or stop serving it\n  }\n  throw error\n}","preventionTips":["Make allow rules a subset of mount coverage — allowed implies servable","Mount workspace/symlinked package directories explicitly"],"tags":["asset-server","mounts","routing","files"],"backgroundTag":"path-outside-configured-root","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}