{"record":{"id":"d5b0d4697311c424","repo":"abhigyanpatwari/GitNexus","slug":"compound-engineering-plugin-component-must-be-a-re","errorCode":null,"errorMessage":"Compound Engineering plugin component must be a real directory: {directory}","messagePattern":"Compound Engineering plugin component must be a real directory: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/runtime_mounts.py","lineNumber":344,"sourceCode":"            if stat.S_ISLNK(metadata.st_mode):\n                raise SandboxError(f\"Compound Engineering plugin entries must not be symlinks: {entry.path}\")\n            if stat.S_ISDIR(metadata.st_mode):\n                yield from walk(Path(entry.path), relative)\n            elif stat.S_ISREG(metadata.st_mode):\n                yield relative, Path(entry.path)\n            else:\n                raise SandboxError(f\"Compound Engineering plugin entries must be regular files: {entry.path}\")\n\n    for name in _ALLOWED_PLUGIN_DIRS:\n        directory = source / name\n        if not directory.exists():\n            continue\n        try:\n            metadata = directory.lstat()\n        except OSError as exc:\n            raise SandboxError(f\"Compound Engineering plugin component is unreadable: {directory}: {exc}\") from exc\n        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):\n            raise SandboxError(f\"Compound Engineering plugin component must be a real directory: {directory}\")\n        yield from walk(directory, PurePosixPath(name))\n\n\ndef _bounded_plugin_bytes(path: Path) -> tuple[bytes, bool]:\n    \"\"\"Read one stable regular file without following a last-component symlink.\"\"\"\n\n    try:\n        before = path.lstat()\n    except OSError as exc:\n        raise SandboxError(f\"Compound Engineering plugin file is unreadable: {path}: {exc}\") from exc\n    if stat.S_ISLNK(before.st_mode) or not stat.S_ISREG(before.st_mode):\n        raise SandboxError(f\"Compound Engineering plugin file must be regular and non-symlink: {path}\")\n    if before.st_size > MAX_CE_PLUGIN_FILE_BYTES:\n        raise SandboxError(f\"Compound Engineering plugin file exceeds the per-file limit: {path}\")\n    descriptor = os.open(path, os.O_RDONLY | getattr(os, \"O_NOFOLLOW\", 0))\n    try:\n        opened = os.fstat(descriptor)\n        if (opened.st_dev, opened.st_ino) != (before.st_dev, before.st_ino) or not stat.S_ISREG(opened.st_mode):","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runtime_mounts.py#L326-L362","documentation":"When a component directory exists and lstat succeeds, the harness rejects it if the mode is a symlink or not a directory. The top-level skills/, scripts/, assets/ entries themselves must be real directories — the per-entry symlink ban (error 535) covers their contents.","triggerScenarios":"<plugin_dir>/skills (or scripts, assets) is a symlink to another location, or is a regular file.","commonSituations":"Operator symlinked skills/ from a shared location; plugin assembled with components as links rather than real trees; monorepo workspace link leaking into the plugin.","solutions":["Check: `ls -ld <plugin_dir>/skills <plugin_dir>/scripts <plugin_dir>/assets` — modes must start with `d`, not `l`.","Replace each symlink with a real directory: `rm <plugin_dir>/skills && cp -r <real_skills> <plugin_dir>/skills`.","Re-extract the plugin with `tar -xhf` to dereference symlinks at extraction time."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path\n\ndef components_are_real_dirs(source: Path) -> bool:\n    for name in (\"skills\", \"scripts\", \"assets\"):\n        d = source / name\n        if not d.exists():\n            continue\n        try:\n            mode = d.lstat().st_mode\n        except OSError:\n            return False\n        if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):\n            return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    list(_plugin_files(source))\nexcept SandboxError as exc:\n    if \"component must be a real directory\" in str(exc):\n        # replace the symlink/file with a real directory\n        ...\n    raise","preventionTips":["Never symlink the top-level skills/scripts/assets directories.","Extract plugin tarballs with `tar -xhf` to dereference at extraction time."],"tags":["symlink-guard","ce-plugin","sandbox","structure"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}