Skip to content

fix(resolver): skip inaccessible ancestor directories instead of fatally erroring - #28228

Closed
robobun wants to merge 2 commits into
mainfrom
claude/fix-28220-landlock-eacces
Closed

robobun wants to merge 2 commits into
mainfrom
claude/fix-28220-landlock-eacces

Conversation

@robobun

@robobun robobun commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • When running in sandboxed environments (e.g., Landlock), ancestor directories like / or /home may not be readable even though getcwd() succeeds and the CWD itself is fully accessible
  • Previously, the resolver's directory tree walk treated EACCES on any ancestor directory as fatal, returning null and causing CouldntReadCurrentDirectory
  • Now, EACCES/AccessDenied errors during ancestor directory walking are handled gracefully: the directory is cached as "not found" and the walk continues to the next directory in the queue

Root cause

In src/resolver/resolver.zig, the dirInfoCachedMaybeLog function walks from the filesystem root (/) down to the CWD, opening each directory with openDirAbsoluteZ(). When any directory returns EACCES, the code fell into the generic error handler which returned null for the entire operation, cascading up to a fatal CouldntReadCurrentDirectory error.

Fix

Added a specific EACCES/AccessDenied error case in the directory walk that:

  1. Caches the inaccessible directory as "not found" (preventing repeated open attempts)
  2. Signals the outer loop to skip the directory and continue walking toward the CWD
  3. Replaced unreachable at the end of the function with return null for safety

Config file discovery (package.json, tsconfig.json, bunfig.toml) in inaccessible parent directories gracefully degrades — they simply won't be found, which is correct behavior in a sandboxed environment.

Test plan

  • Added regression test (test/regression/issue/028220.test.ts) using Linux Landlock to create a sandbox where only the CWD is accessible
  • Test passes with debug build (bun bd test), fails with system bun (USE_SYSTEM_BUN=1)
  • Verified bun run and bun run with require() both work under Landlock sandbox
  • Existing resolver tests pass unchanged

Closes #28220

🤖 Generated with Claude Code

…lly erroring

When running in sandboxed environments (e.g., Landlock), ancestor
directories like / or /home may not be readable even though getcwd()
succeeds and the CWD is fully accessible. Previously, the resolver's
directory tree walk treated EACCES on any ancestor directory as fatal,
returning null and causing "CouldntReadCurrentDirectory". Now, EACCES
is handled by caching the directory as not found and continuing the
walk, allowing bun to function in restricted filesystem environments.

Closes #28220

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

This PR fixes bun's handling of inaccessible ancestor directories during path resolution. Instead of fatally erroring on EACCES when opening parent directories, the resolver now gracefully skips unreadable directories and continues traversing. A regression test validates the fix in a Landlock sandbox environment.

Changes

Cohort / File(s) Summary
Resolver error handling
src/resolver/resolver.zig
Introduces optional file descriptor handling via maybe_open_dir result. When EACCES occurs during directory opening, the directory is cached as not found and traversal continues instead of aborting. End of resolution loop returns null on failure rather than unreachable branch.
Regression test
test/regression/issue/028220.test.ts
New test file for Landlock sandbox scenario. Verifies bun run does not fatal error when ancestor directories are unreadable. Includes Landlock helper C program, compilation step, subsystem support check, and two test cases: simple script execution and script with require() calls.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: skipping inaccessible ancestor directories instead of fatally erroring during resolver path traversal.
Description check ✅ Passed The description comprehensively addresses both template sections: it clearly explains what the PR does (handling EACCES gracefully) and provides detailed verification through regression testing and existing test validation.
Linked Issues check ✅ Passed The pull request fully addresses issue #28220 by implementing graceful EACCES handling during ancestor directory traversal, allowing bun to function in sandboxed environments while maintaining best-effort config discovery.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the issue: resolver error handling for EACCES, a regression test for the Landlock scenario, and no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@robobun

robobun commented Mar 18, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 6:23 AM PT - Mar 18th, 2026

❌ @autofix-ci[bot], your commit 1005a49 has 3 failures in Build #39969 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 28228

That installs a local version of the PR into your bun-28228 executable, so you can run:

bun-28228 --bun

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/resolver/resolver.zig`:
- Around line 2840-2847: The EACCES branch is currently treating permission
errors on queue_top as "not found" unconditionally; change it to
preserve/propagate EACCES when queue_top is the final requested directory. In
the error.EACCES / error.AccessDenied arm, check the remaining queue length
(e.g., queue_slice.len or equivalent) and only run the cache-and-skip logic
(rfs.entries.getOrPut(queue_top.unsafe_path),
r.dir_cache.markNotFound(queue_top.result), rfs.entries.markNotFound(...), break
:open_dir null) when there are deeper entries left in the queue; otherwise
return or propagate the original EACCES (preserve the old fatal/error path)
instead of caching a miss.

In `@test/regression/issue/028220.test.ts`:
- Around line 101-127: The suite setup currently lives in the standalone test
"compile landlock helper" which initializes helperPath and landlockSupported;
move this logic into a proper beforeAll hook or, preferably, inline the
compile-and-check steps at the start of each behavioral test so no test depends
on another. Specifically, take the body of test("compile landlock helper")
(mkdirSync, writeFileSync of LANDLOCK_HELPER_SRC, the Bun.spawnSync gcc compile,
expect checks, and the landlock check that sets landlockSupported) and either
(A) place it inside a beforeAll block that sets helperPath and landlockSupported
for the suite, or (B) copy the same steps into each test that needs them so each
test independently creates the temp dir, compiles via Bun.spawnSync, asserts
exitCode/existsSync, runs the helper to detect LANDLOCK_UNSUPPORTED, and
sets/uses landlockSupported locally.
- Around line 92-96: Replace hard-coded paths /home and /tmp with a
harness-provided temporary directory: change the TEST_BASE constant (currently
"/home/bun-test-user/project") to be derived from the test harness tempDir and
use that tempDir when creating directories (the mkdirSync calls around the
TEST_BASE usage and the similar calls at lines 103-105). Ensure you call the
harness tempDir factory once, create the nested project path under it, and
update any assertions/cleanup to use that temp path so ancestors remain outside
the Landlock allow list while avoiding fixed global paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 18ac231a-5105-4123-93e1-2a8c6ec11438

📥 Commits

Reviewing files that changed from the base of the PR and between 76f4f38 and 1005a49.

📒 Files selected for processing (2)
  • src/resolver/resolver.zig
  • test/regression/issue/028220.test.ts

Comment thread src/resolver/resolver.zig
Comment on lines +2840 to +2847
error.EACCES, error.AccessDenied => {
// In sandboxed environments (e.g., Landlock), ancestor directories
// may not be readable even though the CWD is accessible. Cache as
// not found and skip this directory rather than treating it as fatal.
const cached_dir_entry_result = rfs.entries.getOrPut(queue_top.unsafe_path) catch unreachable;
r.dir_cache.markNotFound(queue_top.result);
rfs.entries.markNotFound(cached_dir_entry_result);
break :open_dir null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve EACCES on the requested directory itself.

This branch also runs on the final queue_top entry, so a direct permission failure on the requested directory now gets cached as a miss and silently treated like “not found.” The skip should only apply while there are deeper entries left in queue_slice; otherwise keep the old fatal/error path.

💡 Suggested guard
-                    error.EACCES, error.AccessDenied => {
+                    error.EACCES, error.AccessDenied => {
+                        if (queue_slice.len == 0) {
+                            const cached_dir_entry_result = rfs.entries.getOrPut(queue_top.unsafe_path) catch unreachable;
+                            r.dir_cache.markNotFound(queue_top.result);
+                            rfs.entries.markNotFound(cached_dir_entry_result);
+                            if (comptime enable_logging) {
+                                r.log.addErrorFmt(
+                                    null,
+                                    logger.Loc{},
+                                    r.allocator,
+                                    "Cannot read directory \"{s}\": {s}",
+                                    .{ queue_top.unsafe_path, `@errorName`(err) },
+                                ) catch {};
+                            }
+                            return null;
+                        }
                         // In sandboxed environments (e.g., Landlock), ancestor directories
                         // may not be readable even though the CWD is accessible. Cache as
                         // not found and skip this directory rather than treating it as fatal.
                         const cached_dir_entry_result = rfs.entries.getOrPut(queue_top.unsafe_path) catch unreachable;
                         r.dir_cache.markNotFound(queue_top.result);
                         rfs.entries.markNotFound(cached_dir_entry_result);
                         break :open_dir null;
                     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/resolver/resolver.zig` around lines 2840 - 2847, The EACCES branch is
currently treating permission errors on queue_top as "not found"
unconditionally; change it to preserve/propagate EACCES when queue_top is the
final requested directory. In the error.EACCES / error.AccessDenied arm, check
the remaining queue length (e.g., queue_slice.len or equivalent) and only run
the cache-and-skip logic (rfs.entries.getOrPut(queue_top.unsafe_path),
r.dir_cache.markNotFound(queue_top.result), rfs.entries.markNotFound(...), break
:open_dir null) when there are deeper entries left in the queue; otherwise
return or propagate the original EACCES (preserve the old fatal/error path)
instead of caching a miss.

Comment on lines +92 to +96
// Use a path under /home where intermediate ancestors (/home, /home/bun-test-user)
// are NOT in the Landlock allow list. This triggers the bug because the resolver
// walks up to / and tries to openat each ancestor directory.
const TEST_BASE = "/home/bun-test-user/project";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use harness temp dirs instead of fixed /home and /tmp paths.

mkdirSync("/home/bun-test-user/project", ...) will fail on many non-root Linux environments, and both globals are shared across reruns/workers. A tempDir-backed sandbox still reproduces the bug because its ancestors remain outside the Landlock allow list.

As per coding guidelines, "Use tempDir from harness to create temporary directories; do not use tmpdirSync or fs.mkdtempSync."

Also applies to: 103-105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/regression/issue/028220.test.ts` around lines 92 - 96, Replace
hard-coded paths /home and /tmp with a harness-provided temporary directory:
change the TEST_BASE constant (currently "/home/bun-test-user/project") to be
derived from the test harness tempDir and use that tempDir when creating
directories (the mkdirSync calls around the TEST_BASE usage and the similar
calls at lines 103-105). Ensure you call the harness tempDir factory once,
create the nested project path under it, and update any assertions/cleanup to
use that temp path so ancestors remain outside the Landlock allow list while
avoiding fixed global paths.

Comment on lines +101 to +127
// Compile the Landlock helper once before all tests
test("compile landlock helper", () => {
mkdirSync("/tmp/landlock-helper", { recursive: true });
const srcPath = "/tmp/landlock-helper/landlock_sandbox.c";
helperPath = "/tmp/landlock-helper/landlock_sandbox";

writeFileSync(srcPath, LANDLOCK_HELPER_SRC);

const result = Bun.spawnSync({
cmd: ["gcc", "-o", helperPath, srcPath],
env: bunEnv,
});
expect(result.exitCode).toBe(0);
expect(existsSync(helperPath)).toBe(true);

// Check if Landlock is supported on this kernel
mkdirSync(TEST_BASE, { recursive: true });
writeFileSync(join(TEST_BASE, "check.js"), "console.log('ok');\n");
const check = Bun.spawnSync({
cmd: [helperPath, TEST_BASE, dirname(bunExe()), bunExe(), "-e", "console.log('test')"],
env: bunEnv,
cwd: TEST_BASE,
});
if (check.stdout.toString().includes("LANDLOCK_UNSUPPORTED")) {
landlockSupported = false;
}
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Don't use a separate test() as suite setup.

helperPath and landlockSupported are only initialized by "compile landlock helper". Running either behavioral case with a name filter skips that setup and leaves the suite in an invalid state. Inline the small setup per test, or at minimum move it to beforeAll.

Based on learnings, "In oven-sh/bun regression tests, prefer explicit per-test setup and per-test assertions (including repeated tempDir, Bun.spawn, bunExe/bunEnv, and checks of stdout, stderr, and exitCode) rather than extracting shared helpers."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/regression/issue/028220.test.ts` around lines 101 - 127, The suite setup
currently lives in the standalone test "compile landlock helper" which
initializes helperPath and landlockSupported; move this logic into a proper
beforeAll hook or, preferably, inline the compile-and-check steps at the start
of each behavioral test so no test depends on another. Specifically, take the
body of test("compile landlock helper") (mkdirSync, writeFileSync of
LANDLOCK_HELPER_SRC, the Bun.spawnSync gcc compile, expect checks, and the
landlock check that sets landlockSupported) and either (A) place it inside a
beforeAll block that sets helperPath and landlockSupported for the suite, or (B)
copy the same steps into each test that needs them so each test independently
creates the temp dir, compiles via Bun.spawnSync, asserts exitCode/existsSync,
runs the helper to detect LANDLOCK_UNSUPPORTED, and sets/uses landlockSupported
locally.

Comment thread src/resolver/resolver.zig
Comment on lines +2840 to +2848
error.EACCES, error.AccessDenied => {
// In sandboxed environments (e.g., Landlock), ancestor directories
// may not be readable even though the CWD is accessible. Cache as
// not found and skip this directory rather than treating it as fatal.
const cached_dir_entry_result = rfs.entries.getOrPut(queue_top.unsafe_path) catch unreachable;
r.dir_cache.markNotFound(queue_top.result);
rfs.entries.markNotFound(cached_dir_entry_result);
break :open_dir null;
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 defer top_parent = queue_top.result at line 2797 executes even when an EACCES directory is skipped via continue, setting top_parent to a result with an Unassigned index. The next accessible directory then gets null from atIndex(Unassigned) as its parent, breaking config inheritance (tsconfig, package.json, etc.) from accessible ancestors above the EACCES gap. Move the top_parent assignment after the continue instead of using defer.

Extended reasoning...

The Bug

The defer top_parent = queue_top.result; statement at line 2797 unconditionally updates top_parent at the end of each loop iteration, including when the loop body hits a continue. In Zig, defer runs on scope exit, which includes continue statements. When an EACCES directory is skipped at line 2887 (const open_dir = maybe_open_dir orelse continue;), the defer still fires, overwriting top_parent with the skipped directory's result.

Why the Result Has an Invalid Index

When a new directory is looked up via getOrPut, the returned Result has index = Unassigned (defined as maxInt(u31) - 1 in src/allocators.zig:75). The markNotFound call at line 2845 only updates the hash map entry — it takes the Result by value, so queue_top.result.index remains Unassigned. The defer then copies this Unassigned-indexed result into top_parent.

Step-by-Step Proof

Consider a mixed-accessibility scenario: / is accessible, /home gets EACCES, /home/user is accessible.

  1. Processing /: Opens successfully, dir_cache.put at line 2967 updates queue_top.result.index to a real index. Defer sets top_parent to /'s result (valid index). / gets a proper DirInfo.

  2. Processing /home: openDirAbsoluteZ returns EACCES. Code breaks to open_dir = null. At line 2887, maybe_open_dir orelse continue triggers continue. The defer fires, setting top_parent = queue_top.result where queue_top.result.index = Unassigned (markNotFound didn't change the index).

  3. Processing /home/user: Opens successfully. At line 2975, r.dir_cache.atIndex(top_parent.index) is called with top_parent.index = Unassigned. Per allocators.zig:605, atIndex returns null for Unassigned indices. So /home/user's DirInfo gets null as its parent.

Impact

With a null parent, /home/user loses inheritance of enclosing_tsconfig_json, enclosing_package_json, package_json_for_browser_field, enclosing_browser_scope, package_json_for_dependencies, and inside_node_modules from /. In the typical Landlock scenario where ALL ancestors are inaccessible, this is mostly harmless since there are no accessible ancestors to inherit from. But in mixed-accessibility scenarios, configs from accessible higher-up directories are silently lost.

Suggested Fix

Replace the defer with an explicit assignment at the end of the successful code path (after line 2979, inside the loop body but only reached when the directory was actually processed). For example, remove line 2797 and add top_parent = queue_top.result; after the dirInfoUncached call around line 2979.

Comment on lines +80 to +83
if (syscall(__NR_landlock_restrict_self, ruleset_fd, 0)) {
fprintf(stdout, "LANDLOCK_UNSUPPORTED\\n");
return 0;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The Landlock helper C program is missing the required prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) call before landlock_restrict_self(). Without it, landlock_restrict_self returns EPERM on non-root processes, which the helper misclassifies as LANDLOCK_UNSUPPORTED, causing the test to silently skip on any non-root Linux system where Landlock IS supported. The fix is adding prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) before the landlock_restrict_self syscall (the standard pattern from all kernel Landlock documentation).

Extended reasoning...

What the bug is

The Landlock helper C program embedded in the test (lines 80-83 of the test file) calls landlock_restrict_self without first calling prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0). The Linux kernel documentation explicitly states that landlock_restrict_self() requires either: (1) the calling thread has set no_new_privs via prctl(PR_SET_NO_NEW_PRIVS), or (2) the thread has CAP_SYS_ADMIN in its current user namespace.

How it manifests

On a non-root Linux system with kernel >= 5.13 (where Landlock IS supported), the following sequence occurs:

  1. landlock_create_ruleset succeeds — the kernel supports Landlock
  2. Rules are added successfully
  3. landlock_restrict_self is called without the no_new_privs attribute set
  4. The kernel returns EPERM because the process lacks both no_new_privs and CAP_SYS_ADMIN
  5. The helper prints LANDLOCK_UNSUPPORTED to stdout
  6. The test reads this output and sets landlockSupported = false (test line 124-126)
  7. Both actual test cases check if (!landlockSupported) and return early with a skip message

Why existing code doesn't prevent it

The error handling at lines 80-83 treats ANY failure from landlock_restrict_self as meaning Landlock is unsupported. This conflates "kernel doesn't support Landlock" with "missing required privilege setup". The EPERM from a missing prctl is indistinguishable from an actual unsupported-kernel error in this code path.

Impact

The regression test provides zero actual coverage on any non-root Linux system where Landlock IS supported. The test likely works in CI (Docker/root environments where CAP_SYS_ADMIN is available) but silently skips for any developer running tests locally as a regular user. This undermines the purpose of the regression test — developers cannot verify the fix locally.

Step-by-step proof

  1. Developer runs bun test test/regression/issue/028220.test.ts on Ubuntu 22.04 (kernel 5.15, Landlock supported) as a regular user
  2. The helper is compiled and executed with Bun.spawnSync
  3. landlock_create_ruleset succeeds (kernel supports it)
  4. landlock_restrict_self fails with EPERM (no prctl, no CAP_SYS_ADMIN)
  5. Helper outputs LANDLOCK_UNSUPPORTED
  6. Test sets landlockSupported = false and both test cases skip
  7. Test suite reports green, but neither sandbox test actually ran

Fix

Add prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) before the landlock_restrict_self syscall, and #include <sys/prctl.h> at the top. This is the standard Landlock setup pattern shown in the kernel documentation (samples/landlock/sandboxer.c) and all official examples.

@@ -0,0 +1,185 @@
import { describe, expect, test } from "bun:test";
import { existsSync, mkdirSync, writeFileSync } from "fs";
import { bunEnv, bunExe } from "harness";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: rmSync is imported but never used (line 3), and check.js is written at line 118 but never referenced (the Landlock check uses -e for inline eval instead). Consider removing the unused import and dead writeFileSync, and adding an afterAll block to clean up /home/bun-test-user and /tmp/landlock-helper.

Extended reasoning...

Unused import

The test imports rmSync from "fs" on line 3, but there are zero calls to rmSync anywhere in the 185-line file. This is dead code — likely a leftover from planned cleanup logic that was never implemented.

Dead check.js file

At line 118, the test writes a check.js file:

writeFileSync(join(TEST_BASE, "check.js"), "console.log('ok');\n");

However, the very next line (120) runs the Landlock support check using -e "console.log('test')" — an inline evaluation flag — instead of executing check.js. The file is created but never referenced by any test command. It appears to be a remnant from an earlier iteration of the test.

Missing cleanup

The test creates directories and files at two locations:

  • /home/bun-test-user/project (lines 117, 135, 164)
  • /tmp/landlock-helper (line 103)

Neither location is cleaned up after the tests complete. The presence of the unused rmSync import strongly suggests cleanup was planned but never wired up. While CI machines are typically ephemeral and the hardcoded /home/bun-test-user/project path is intentional for Landlock testing (ancestor directories must not be in the allow list, ruling out tempDir), an afterAll block would be good hygiene.

Concrete walkthrough

  1. Line 3: import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs"; — rmSync imported
  2. Lines 1-185: No call to rmSync anywhere in the file
  3. Line 118: writeFileSync(join(TEST_BASE, "check.js"), ...) — creates check.js
  4. Line 120: cmd: [helperPath, TEST_BASE, dirname(bunExe()), bunExe(), "-e", "console.log('test')"] — uses -e inline eval, not check.js
  5. No afterAll or afterEach block exists in the describe block

Suggested fix

Remove rmSync from the import if no cleanup is added, or (better) add an afterAll block:

afterAll(() => {
  rmSync("/home/bun-test-user", { recursive: true, force: true });
  rmSync("/tmp/landlock-helper", { recursive: true, force: true });
});

Also remove the dead writeFileSync(join(TEST_BASE, "check.js"), ...) on line 118.

@flora131

flora131 commented Apr 5, 2026

Copy link
Copy Markdown

Hitting this in a DevContainer environment where bun run fails with CouldntReadCurrentDirectory when the container restricts access to ancestor directories. This is blocking us from using Bun in our CI/sandboxed workflows. Would love to see this merged. Happy to test a build if that helps.

@BenjaBobs

Copy link
Copy Markdown

I made this PR instead that (imo) fixes this issue in a better way: #28782, waiting for maintainers to take it to the next step

@BenjaBobs

Copy link
Copy Markdown

@robobun
I think this PR can be closed in favor of #28782, since the code base is no longer in zig (as this pr is), and the relevant config resolver code has been split out into a separate file.

@robobun

robobun commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: this PR's implementation lives entirely in Zig source files that have since been removed from the tree as part of the Rust migration. The change can no longer merge cleanly and the files it edits no longer exist on main.

If the underlying issue is still present, it will need a fresh fix against the Rust implementation.

@robobun robobun closed this Jun 26, 2026
@BenjaBobs

Copy link
Copy Markdown

@robobun
Does this mean focus shifts to #28782 instead?
Or will #28220 be fixed in some other way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bun run fails with CouldntReadCurrentDirectory when ancestor directories are not readable

3 participants