{"record":{"id":"b38bb5df791dfde9","repo":"abhigyanpatwari/GitNexus","slug":"extra-read-only-mount-must-be-real-and-non-symlink","errorCode":null,"errorMessage":"extra read-only mount must be real and non-symlink: {source}","messagePattern":"extra read-only mount must be real and non-symlink: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":155,"sourceCode":"                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))\n            ):\n                raise SandboxError(f\"extra read-only mount must be real and non-symlink: {source}\")\n            target = PurePosixPath(mount.target)\n            if not target.is_absolute() or \"..\" in target.parts:\n                raise SandboxError(f\"extra read-only mount target must be absolute: {mount.target}\")\n            additional.append(ReadOnlyMount(source=source, target=target.as_posix()))\n\n        return _sandbox_command_prefix(\n            bwrap=self.bwrap_bin,\n            clone=clone,\n            home=self.home,\n            temp=self.temp,\n            claude_bin=self.claude_host_bin,\n            mounts=(*self.read_only_mounts, *additional),\n            read_only_workspace=read_only_workspace,\n            unshare_network=unshare_network,\n        )\n\n\n_TOKEN_PATTERNS = (","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L137-L173","documentation":"Thrown by `command_prefix_for` after an `extra_read_only_mounts` source was probed successfully but failed the 'real and non-symlink' check: resolved != lexical, `lstat` reports a symlink, or the entry is neither directory nor regular file. The same constraint as error 416, applied to extra mount sources.","triggerScenarios":"An `extra_read_only_mounts` source that is a symlink, resolves through a symlink chain, or is a socket/FIFO/device rather than a dir or regular file. Bubblewrap needs a real inode to bind.","commonSituations":"Evidence file replaced with a symlink to a shared store; `/var/lib/.../oracle` is a symlink; an oracle written as a fifo from a streaming producer; a path that crosses `/proc` or `/sys` symlinks.","solutions":["Dereference the source: `source = ReadOnlyMount(source=p.resolve(strict=True), target=t)`.","Write evidence as a regular file (not a symlink) and freeze that path.","Filter: `assert (p.is_dir() or p.is_file()) and not p.is_symlink()` before constructing the mount.","If the source is a symlinked directory by design, replace it with a real directory."],"exampleFix":"# before: oracle is a symlink\nmount = ReadOnlyMount(source=Path('/evidence/oracle'), target='/workspace/oracle.json')\n# /evidence/oracle -> /shared/oracle  (symlink)\nprefix = session.command_prefix_for(extra_read_only_mounts=[mount])  # -> SandboxError\n# after: bind the resolved real path\nmount = ReadOnlyMount(\n    source=Path('/evidence/oracle').resolve(strict=True),\n    target='/workspace/oracle.json',\n)\nprefix = session.command_prefix_for(extra_read_only_mounts=[mount])","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path\n\ndef is_real_nonsymlink_path(p: Path) -> bool:\n    try:\n        source = p.expanduser().absolute()\n        meta = source.lstat()\n        resolved = source.resolve(strict=True)\n    except OSError:\n        return False\n    return (\n        resolved == source\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_extra_mount_source(p: Path) -> bool:\n    return is_real_nonsymlink_path(p)","tryCatchPattern":"try:\n    prefix = session.command_prefix_for(extra_read_only_mounts=mounts)\nexcept SandboxError as exc:\n    if \"extra read-only mount must be real and non-symlink\" in str(exc):\n        # dereference symlinks, drop non-regular entries\n        mounts = [\n            ReadOnlyMount(source=m.source.resolve(strict=True), target=m.target)\n            for m in mounts\n            if is_real_nonsymlink_path(m.source.resolve(strict=True))\n        ]\n        prefix = session.command_prefix_for(extra_read_only_mounts=mounts)\n    raise","preventionTips":["Always `resolve(strict=True)` extra mount sources before constructing `ReadOnlyMount`.","Write evidence as regular files, never symlinks or sockets.","Validate `not p.is_symlink() and (p.is_dir() or p.is_file())` in your harness."],"tags":["sandbox","bwrap","symlink","mount","path-validation"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}