Current Behavior
When using negated patterns in outputs to exclude a subdirectory from caching, the excluded directory is still stored in the Nx cache.
Example project.json:
{
"build": {
"executor": "@nx/next:build",
"outputs": ["{options.outputPath}", "!{options.outputPath}/.next/cache"],
"options": {
"outputPath": "dist/apps/my-app"
}
}
}
After running nx run my-app:build, inspecting the Nx cache entry reveals that .next/cache (often 600MB+) is still present:
.nx/cache/<hash>/dist/apps/my-app/.next/cache/ # <-- should not be here
Expected Behavior
The .next/cache directory should not be stored in the Nx cache when excluded via !{options.outputPath}/.next/cache.
GitHub Repo
No response
Steps to Reproduce
- Create a Next.js app in an Nx workspace
- Configure outputs with negation:
["{options.outputPath}", "!{options.outputPath}/.next/cache"]
- Run
nx run my-app:build
- Inspect the cache:
ls .nx/cache/*/dist/apps/my-app/.next/cache
- The
cache directory is present despite the ! exclusion
Nx Report
Node : 22.19.0
OS : darwin-arm64
Native Target : aarch64-macos
npm : 10.9.3
daemon : Disabled
nx : 22.5.4
@nx/js : 22.5.4
@nx/eslint : 22.5.4
@nx/workspace : 22.5.4
@nx/jest : 22.5.4
@nx/devkit : 22.5.4
@nx/eslint-plugin : 22.5.4
@nx/module-federation : 22.5.4
@nx/next : 22.5.4
@nx/react : 22.5.4
@nx/rollup : 22.5.4
@nx/web : 22.5.4
@nx/webpack : 22.5.4
typescript : 5.1.6
---------------------------------------
Community plugins:
@nx-tools/nx-container : 7.2.1
---------------------------------------
Local workspace plugins:
@nx/workspace
@nx/webpack
@nx/eslint
@nx/rollup
@nx/react
@nx/jest
@nx/next
@nx/web
@nx/js
nx
---------------------------------------
Cache Usage: 738.95 MB / 46.04 GB
Failure Logs
Package Manager Version
No response
Operating System
Additional Information
Root Cause
The bug is in the interaction between expand_outputs (packages/nx/src/native/cache/expand_outputs.rs) and _copy in cache.rs:
expand_outputs correctly partitions entries into regular and negated globs
- The walker correctly excludes files under the negated path (
.next/cache/*)
- However, the walker still returns intermediate directory entries like
.next itself
- In
cache.rs put(), each expanded output is copied via _copy() — and when _copy receives a directory, it calls copy_dir_all() which recursively copies everything inside, re-including the negated .next/cache subdirectory
The existing test should_handle_multiple_outputs_with_negation only validates the output of expand_outputs in isolation — it doesn't account for how put() uses directory entries with _copy.
Suggested Fix
In expand_outputs.rs, after collecting found_paths, filter out directory entries that are ancestors of negated paths:
let found_paths = found_paths
.into_iter()
.filter(|path| {
let full_path = directory.join(path);
if !full_path.is_dir() {
return true;
}
let path_with_slash = format!("{}/", path);
!negated_globs.iter().any(|neg| neg.starts_with(&path_with_slash))
})
.collect::<Vec<_>>();
Environment
- Nx version: 22.5.4 (likely affects all versions with
DbCache)
- OS: macOS (darwin-arm64)
- Node: 22.x
Related
Current Behavior
When using negated patterns in
outputsto exclude a subdirectory from caching, the excluded directory is still stored in the Nx cache.Example
project.json:{ "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}", "!{options.outputPath}/.next/cache"], "options": { "outputPath": "dist/apps/my-app" } } }After running
nx run my-app:build, inspecting the Nx cache entry reveals that.next/cache(often 600MB+) is still present:Expected Behavior
The
.next/cachedirectory should not be stored in the Nx cache when excluded via!{options.outputPath}/.next/cache.GitHub Repo
No response
Steps to Reproduce
["{options.outputPath}", "!{options.outputPath}/.next/cache"]nx run my-app:buildls .nx/cache/*/dist/apps/my-app/.next/cachecachedirectory is present despite the!exclusionNx Report
Failure Logs
Package Manager Version
No response
Operating System
Additional Information
Root Cause
The bug is in the interaction between
expand_outputs(packages/nx/src/native/cache/expand_outputs.rs) and_copyincache.rs:expand_outputscorrectly partitions entries into regular and negated globs.next/cache/*).nextitselfcache.rsput(), each expanded output is copied via_copy()— and when_copyreceives a directory, it callscopy_dir_all()which recursively copies everything inside, re-including the negated.next/cachesubdirectoryThe existing test
should_handle_multiple_outputs_with_negationonly validates the output ofexpand_outputsin isolation — it doesn't account for howput()uses directory entries with_copy.Suggested Fix
In
expand_outputs.rs, after collectingfound_paths, filter out directory entries that are ancestors of negated paths:Environment
DbCache)Related
.next/cachein cache)