Skip to content

[All Platforms] openclaw plugins install fails inside sandbox: "Invalid extensions directory: base directory must be a real directory" #2203

@paritoshd-nv

Description

@paritoshd-nv

Description

Any openclaw plugins install command for a ClawHub-hosted plugin fails inside a NemoClaw sandbox — on any policy tier — at the post-extract validation step with:

Invalid extensions directory: base directory must be a real directory

Steps to reproduce

  1. Onboard any sandbox with nemoclaw onboard.
  2. Open the sandbox and run:

openclaw plugins install @openclaw/diagnostics-otel

  1. Install proceeds through ClawHub resolution → download → extract.
  2. Fails at the final install-path validation. Actual output
  Resolving clawhub:@openclaw/diagnostics-otel…
  ClawHub code-plugin @openclaw/diagnostics-otel@2026.3.22 channel=official verification=source-linked
  Compatibility: pluginApi=>=2026.3.22
  Downloading plugin @openclaw/diagnostics-otel@2026.3.22 from ClawHub…
  Extracting /tmp/openclaw-clawhub-package-yCnWVv/diagnostics-otel.zip…
  Invalid extensions directory: base directory must be a real directory

Expected

Plugin installs into ~/.openclaw/extensions// and appears in openclaw plugins list.

Root cause

NemoClaw's sandbox image symlinks every persistent state path under ~/.openclaw/ into /sandbox/.openclaw-data/ so state survives sandbox recreation. In a fresh sandbox, ls -la ~/.openclaw shows:

  lrwxrwxrwx 1 root root 30 Apr 14 01:21 agents      -> /sandbox/.openclaw-data/agents
  lrwxrwxrwx 1 root root 30 Apr 14 01:21 canvas      -> /sandbox/.openclaw-data/canvas
  lrwxrwxrwx 1 root root 35 Apr 14 01:21 credentials -> /sandbox/.openclaw-data/credentials
  lrwxrwxrwx 1 root root 28 Apr 14 01:21 cron        -> /sandbox/.openclaw-data/cron
  ...
  lrwxrwxrwx 1 root root 34 Apr 14 01:21 extensions  -> /sandbox/.openclaw-data/extensions
  ...

Every stateful subdirectory is a symlink.

OpenClaw's plugin-install validator at openclaw/src/infra/install-safe-path.ts:93-96 explicitly rejects symlinks as install bases:

const baseLstat = await fs.lstat(baseDir);
if (!baseLstat.isDirectory() || baseLstat.isSymbolicLink()) {
throw new Error(Invalid ${params.boundaryLabel}: base directory must be a real directory);
}

This is TOCTOU hardening in OpenClaw — a reasonable security default in isolation, but it's structurally incompatible with NemoClaw's symlink-based persistence layout.

Reproduction Steps

  1. Onboard any sandbox with nemoclaw onboard.
  2. Open the sandbox and run:

openclaw plugins install @openclaw/diagnostics-otel

  1. Install proceeds through ClawHub resolution → download → extract.
  2. Fails at the final install-path validation.

Environment

  • NemoClaw: v0.0.21 (reproduced on current main)
  • OpenClaw: 2026.4.2 (d74a122)
  • Sandbox: NemoClaw-default sandbox image (openclaw-sandbox.yaml base policy)
  • Policy tier: reproduced on both Balanced (with npm preset) and Restricted (no presets)
  • Plugin source: ClawHub (reproduces with any ClawHub-hosted plugin)

Debug Output

Logs

Checklist

  • I confirmed this bug is reproducible
  • I searched existing issues and this is not a duplicate

Metadata

Metadata

Assignees

Labels

area: sandboxOpenShell sandbox lifecycle, runtime, config, or recoveryintegration: openclawOpenClaw integration behavior

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions