Skip to content

Caching routes with fs or fs-lite fails when a route is both a path and a prefix of another path #4142

@maxmalysh

Description

@maxmalysh

Environment

Node.js v25.6.1
nitropack 2.13.2
macOS

Reproduction

https://stackblitz.com/github/maxmalysh/nitro-fs-lite-enotdir

Describe the bug

It's not possible to cache both /foo and /foo/bar when using fs or fs-lite as the storage driver:

nitro: {
  storage: {
      // Broken because of the bug described below
    cache: { driver: 'fs-lite', base: '.cache/nitro' }
  }
}

Caching /foo creates a file on disk. Then caching /foo/bar needs /foo to be a directory — fails with ENOTDIR.

This makes fs or fs-lite unusable for route caching whenever a route can be both a leaf and a prefix of another route (e.g. /en and /en/about, or /items/123 and /items/123/details).

Steps to reproduce:

  1. Open the StackBlitz reproduction (or clone https://github.com/maxmalysh/nitro-fs-lite-enotdir)
  2. Visit /foo — payload is cached successfully
  3. Visit /foo/bar — crashes with ENOTDIR

Expected: Both routes cache their payloads successfully.

Actual:

{                                                                                                                 
  "error": "ENOTDIR: not a directory, open '.../.cache/nitro/foo/bar'",
  "code": "ENOTDIR"                                                                                               
}                                                               

Note: unstorage's key-to-path mapping (: → /) is by design. The question is whether nitro should normalize/hash   
keys for filesystem drivers, or document that fs/fs-lite are unsafe for route-based caching.

Additional context

Real-world impact:

Logs

ℹ Error: ENOTDIR: not a directory, open './.nitro/cache/foo/bar'

 ⁃ (ENOTDIR: not a directory, open '.nitro/cache/foo/bar':undefined:undefined)
 ⁃ (at Error: ENOTDIR: not a directory, open '.nitro/cache/foo/bar':undefined:undefined:undefined:undefined)

[CAUSE]
Error {
  code: 'ENOTDIR',
  errno: -20,
  path: './.nitro/cache/foo/bar',
  syscall: 'open',
  message: "ENOTDIR: not a directory, open 
  './.nitro/cache/foo/bar'",
  stack: "ENOTDIR: not a directory, open './.nitro/cache/foo/bar'\n" +
  "at Error: ENOTDIR: not a directory, open './.nitro/cache/foo/bar':undefined:undefined)",
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions