remix-run/remix · error · AssetServerCompilationError

FILE_OUTSIDE_MOUNTS

FILE_OUTSIDE_MOUNTS

Error message

File ${identityPath} is outside all configured mounts.

What it means

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.

Source

Thrown at packages/assets/src/lib/files/compiler.ts:571

    })
  }

  if (!args.isAllowed(identityPath)) {
    throw createAssetServerCompilationError(
      `File "${identityPath}" is not allowed by the asset server access configuration. ` +
        `Add a matching allowFiles or allowPackages rule, or remove a conflicting denyFiles rule.`,
      {
        code: 'FILE_NOT_ALLOWED',
      },
    )
  }

  let stableUrlPathname = args.routes.toUrlPathname(identityPath)
  if (!stableUrlPathname) {
    throw createAssetServerCompilationError(
      `File ${identityPath} is outside all configured mounts.`,
      {
        code: 'FILE_OUTSIDE_MOUNTS',
      },
    )
  }

  return {
    identityPath,
    stableUrlPathname,
  }
}

export function createResponseForFile(
  result: FileCompileResult,
  options: {
    cacheControl: string
    ifNoneMatch: string | null
    method: string
  },
): Response {

View on GitHub (pinned to 9696913134)

Solutions

  1. Add or extend a mount so the file's directory is covered, e.g. mount the assets directory in the file routes config
  2. Align overly-broad allowFiles globs with the actual mount roots so allowed files are always servable
  3. If the file comes from a linked/workspace package, mount that package's directory as well

Example fix

// before
routes: createFileRoutes({ mounts: { '/assets': 'app/assets' } })
// file lives in app/private/logo.svg → outside all mounts

// after
routes: createFileRoutes({ mounts: { '/assets': 'app/assets', '/private': 'app/private' } })
Defensive patterns

Strategy: validation

Validate before calling

// Ensure each identity path falls under a mount root before compiling:
for (const file of filesToServe) {
  if (!mounts.some(([, root]) => isWithin(root, file))) {
    throw new Error(`configure a mount covering ${file}`)
  }
}

Type guard

function isWithin(root: string, path: string): boolean {
  const rel = path.slice(root.length)
  return path.startsWith(root) && (rel === '' || rel.startsWith('/'))
}

Try / catch

try {
  const resolved = compiler.resolvedFile(file)
} catch (error) {
  if (error?.code === 'FILE_OUTSIDE_MOUNTS') {
    // add a mount for the file's directory, or stop serving it
  }
  throw error
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/e9e2652b16b510f5. Report an issue: GitHub.