{"record":{"id":"0370358f26e2dfbf","repo":"abhigyanpatwari/GitNexus","slug":"read-only-sandbox-path-must-be-real-and-non-symlin","errorCode":null,"errorMessage":"read-only sandbox path must be real and non-symlink: {raw_path}","messagePattern":"read-only sandbox path must be real and non-symlink: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":135,"sourceCode":"        for harness-owned, post-session evidence such as hidden oracles.\n        \"\"\"\n\n        additional: list[ReadOnlyMount] = []\n        clone = _real_directory(self.clone, label=\"sandbox clone\")\n        for raw_path in read_only_paths:\n            lexical = raw_path.expanduser().absolute()\n            try:\n                relative = lexical.relative_to(clone)\n                metadata = lexical.lstat()\n                resolved = lexical.resolve(strict=True)\n            except (OSError, ValueError) as exc:\n                raise SandboxError(f\"read-only sandbox path is unavailable: {raw_path}\") from exc\n            if (\n                resolved != lexical\n                or stat.S_ISLNK(metadata.st_mode)\n                or not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode))\n            ):\n                raise SandboxError(f\"read-only sandbox path must be real and non-symlink: {raw_path}\")\n            additional.append(\n                ReadOnlyMount(\n                    source=lexical,\n                    target=f\"{SANDBOX_WORKSPACE}/{PurePosixPath(relative.as_posix())}\",\n                )\n            )\n\n        for mount in extra_read_only_mounts:\n            source = mount.source.expanduser().absolute()\n            try:\n                metadata = source.lstat()\n                resolved = source.resolve(strict=True)\n            except OSError as exc:\n                raise SandboxError(f\"extra read-only mount is unavailable: {source}\") from exc\n            if (\n                resolved != source\n                or stat.S_ISLNK(metadata.st_mode)\n                or not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode))","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L117-L153","documentation":"Thrown by `command_prefix_for` after a `read_only_paths` entry resolved successfully but failed the 'real and non-symlink' check: the resolved path differs from the lexical path (symlink traversal), `lstat` reports a symlink, or the entry is neither a directory nor a regular file. Bubblewrap bind mounts must point at real on-disk inodes.","triggerScenarios":"Passing a symlink as a `read_only_paths` entry; a path whose `resolve(strict=True)` crosses a symlink (`resolved != lexical`); a path that is a socket/FIFO/device file (not dir/regular); a path that is itself a symlink to a directory.","commonSituations":"The clone contains a symlink (e.g. `node_modules` linked elsewhere) that the caller tried to freeze; `/tmp` paths that are symlinked; a path that resolves through `/usr/local/...` symlink chains; a fifo/socket the harness mistook for a file.","solutions":["Pass the real target of the symlink: `p = p.resolve(strict=True)` and use the resolved path.","Remove or replace the symlink with the real file/dir inside the clone before freezing.","Filter out non-regular, non-directory entries: `assert p.is_dir() or p.is_file()` and `not p.is_symlink()`.","If you genuinely need a symlink frozen, copy its target into the clone as a regular file."],"exampleFix":"# before: p is a symlink\nprefix = session.command_prefix_for(read_only_paths=[p])  # -> SandboxError\n# after: dereference first\nreal = p.expanduser().resolve(strict=True)\nassert real.is_dir() or real.is_file()\nprefix = session.command_prefix_for(read_only_paths=[real])","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path\n\ndef is_real_nonsymlink_under_clone(clone: Path, p: Path) -> bool:\n    try:\n        lexical = p.expanduser().absolute()\n        lexical.relative_to(clone)\n        meta = lexical.lstat()\n        resolved = lexical.resolve(strict=True)\n    except (OSError, ValueError):\n        return False\n    return (\n        resolved == lexical\n        and not stat.S_ISLNK(meta.st_mode)\n        and (stat.S_ISDIR(meta.st_mode) or stat.S_ISREG(meta.st_mode))\n    )","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_freezable_real_path(clone: Path, p: Path) -> bool:\n    \"\"\"Narrow to paths that command_prefix_for will accept.\"\"\"\n    if not is_real_nonsymlink_under_clone(clone, p):\n        return False\n    return True","tryCatchPattern":"try:\n    prefix = session.command_prefix_for(read_only_paths=paths)\nexcept SandboxError as exc:\n    if \"must be real and non-symlink\" in str(exc):\n        # dereference symlinks / drop sockets, then retry\n        paths = [p.expanduser().resolve(strict=True) for p in paths\n                 if is_real_nonsymlink_under_clone(session.clone, p.expanduser().resolve(strict=True))]\n        prefix = session.command_prefix_for(read_only_paths=paths)\n    raise","preventionTips":["Dereference symlinks before passing them: `p = p.resolve(strict=True)`.","Filter out sockets/fifos/devices — only dirs and regular files are freezable.","Avoid symlinks inside the clone for paths you intend to freeze."],"tags":["sandbox","bwrap","symlink","path-validation","mount"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}