Description
Summary:
Sandbox image build fails deterministically at Step 17/56 on Apple Silicon macOS. Patch 4 in Step 17 attempts to wrap a specific source pattern in OpenClaw's replaceConfigFile, but that pattern does not exist in the bundled OpenClaw 2026.4.24. The build aborts before sandbox creation completes.
This blocks all Apple Silicon macOS users from creating a new sandbox via nemoclaw onboard, including fresh installs and rebuilds of previously-working sandboxes.
Reproduction Steps
Steps to Reproduce
- Fresh install on macOS Apple Silicon:
curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash
- At provider prompt, select option 7 (Local Ollama).
- Select
nemotron-3-nano:30b as the model.
- Name the sandbox (e.g. test)
- Accept default policy preset (Balanced).
Expected Behavior:
Step 17/56 applies all four patches successfully. Build proceeds to Step 18 and completes. Sandbox is registered in the live OpenShell gateway.
Actual Behavior:
Step 17/56 fails at Patch 4 with:
Traceback (most recent call last):
File "<string>", line 1, in <module>
AssertionError: writeConfigFile(params.nextConfig) pattern not found
Build exits 1. Onboard reports Sandbox creation failed (exit 1). Subsequent nemoclaw test connect reports Sandbox 'test' is not present in the live OpenShell gateway. Removed stale local registry entry.
Diagnosis
Patches 1, 2, 3a, and 3b in Step 17 apply successfully (no errors raised). Patch 4 fails because the source pattern it searches for:
javascriptawait writeConfigFile(params.nextConfig, {
...writeOptions,
...params.writeOptions
});
does not exist in OpenClaw 2026.4.24's replaceConfigFile function. OpenClaw appears to have refactored this function before 2026.4.24 was bundled into the current base image, so the patch was effectively broken against this base image on the day it was published.
Reproducibility:
100%. Reproduces on every nemoclaw onboard attempt, including:
- Fresh installs (no prior NemoClaw state)
- Re-installs after nemoclaw uninstall
- Onboards against existing healthy OpenShell gateways
- Both nemoclaw onboard and nemoclaw onboard --resume
Initial successful onboard occurred on 2026-04-28 (different base image SHA: 331f52d607f4, same bundled OpenClaw version 2026.4.24 per build log). Same failure observed on 2026-05-01 and again on 2026-05-13 against the current base image SHA b8af8a05df0a. The regression appears to have landed in NemoClaw's patch script or the base image rebuild process between April 28 and May 1, and has not been corrected since.
Suggested Fix Directions:
- Update Patch 4 to match OpenClaw 2026.4.24's current replaceConfigFile source structure.
- Or pin OpenClaw to a known-good version in the base image and validate patches against that pin in CI.
- Or make Patch 4 a soft failure (warn and continue) when the pattern isn't found, since the patch only handles an EACCES edge case during plugin metadata persistence (per the patch's own error message: "Config is read-only in sandbox — plugin metadata not persisted").
Environment
Environment:
- OS: macOS Tahoe 26.4.1
- Hardware: MacBook Pro M1 Max, 64 GB unified memory
- Container runtime: Docker Desktop (Apple Virtualization framework)
- NemoClaw: latest installer via curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash (re-pulled 2026-05-13)
- OpenShell CLI: 0.0.36
- Bundled OpenClaw (per Step 16 log): 2026.4.24
- Base image SHA: sha256:b8af8a05df0a65c8932c292cb8b3de02fbd2f837696727602f5ff561217ffe9e
- Selected provider: Local Ollama (option 7)
- Selected model: nemotron-3-nano:30b
Debug Output
## Full Build Log
[6/8] Creating sandbox
──────────────────────────────────────────────────
✓ Updated provider fablab-brave-search
Creating sandbox 'fablab' (this takes a few minutes on first run)...
Pinning base image to sha256:b8af8a05df0a...
Building sandbox image...
Building image openshell/sandbox-from:1778721851 from /private/var/folders/41/9_dlbz895dj8_60h2p7vvly00000gp/T/nemoclaw-build...
Step 1/56 : ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/sandbox-base@sha256:b8af8a05df0a65c8932c292cb8b3de02fbd2f837696727602f5ff5...
Step 2/56 : FROM node:22-slim@sha256:4f77a690f2f8946ab16fe1e791a3ac0667ae1c3575c3e4d0d4589e9ed5bfaf3d AS builder
Step 3/56 : ENV NPM_CONFIG_AUDIT=false NPM_CONFIG_FUND=false NPM_CONFIG_UPDATE_NOTIFIER=false
Step 4/56 : COPY nemoclaw/package.json nemoclaw/package-lock.json nemoclaw/tsconfig.json /opt/nemoclaw/
Step 5/56 : COPY nemoclaw/src/ /opt/nemoclaw/src/
Step 6/56 : WORKDIR /opt/nemoclaw
Step 7/56 : RUN npm ci && npm run build
Step 8/56 : FROM ${BASE_IMAGE}
Step 9/56 : RUN apt-mark manual procps 2>/dev/null || true && (apt-get remove --purge -y gcc gcc-12 g++ g++-12 cpp cpp-12...
Step 10/56 : COPY --from=builder /opt/nemoclaw/dist/ /opt/nemoclaw/dist/
Step 11/56 : COPY nemoclaw/openclaw.plugin.json /opt/nemoclaw/
Step 12/56 : COPY nemoclaw/package.json nemoclaw/package-lock.json /opt/nemoclaw/
Step 13/56 : COPY nemoclaw-blueprint/ /opt/nemoclaw-blueprint/
Step 14/56 : WORKDIR /opt/nemoclaw
Step 15/56 : RUN npm ci --omit=dev
Step 16/56 : RUN set -eu; MIN_VER=$(grep -m 1 'min_openclaw_version' /opt/nemoclaw-blueprint/blueprint.yaml | awk '{print...
Step 17/56 : RUN set -eu; OC_DIST=/usr/local/lib/node_modules/openclaw/dist; fg_export="$(grep -RIlE --include='*.js'...
Sandbox creation failed (exit 1).
Building image openshell/sandbox-from:1778721851 from /private/var/folders/41/9_dlbz895dj8_60h2p7vvly00000gp/T/nemoclaw-build-5lLr38/Dockerfile
Context: /private/var/folders/41/9_dlbz895dj8_60h2p7vvly00000gp/T/nemoclaw-build-5lLr38
Gateway: nemoclaw
Building image openshell/sandbox-from:1778721851 from /private/var/folders/41/9_dlbz895dj8_60h2p7vvly00000gp/T/nemoclaw-build-5lLr38/Dockerfile
Step 1/56 : ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/sandbox-base@sha256:b8af8a05df0a65c8932c292cb8b3de02fbd2f837696727602f5ff561217ffe9e
Step 2/56 : FROM node:22-slim@sha256:4f77a690f2f8946ab16fe1e791a3ac0667ae1c3575c3e4d0d4589e9ed5bfaf3d AS builder
---> 4f77a690f2f8
Step 3/56 : ENV NPM_CONFIG_AUDIT=false NPM_CONFIG_FUND=false NPM_CONFIG_UPDATE_NOTIFIER=false
---> Using cache
---> b8738defa1fb
Step 4/56 : COPY nemoclaw/package.json nemoclaw/package-lock.json nemoclaw/tsconfig.json /opt/nemoclaw/
---> Using cache
---> 8def3736f0e4
Step 5/56 : COPY nemoclaw/src/ /opt/nemoclaw/src/
---> Using cache
---> b5f39786f653
Step 6/56 : WORKDIR /opt/nemoclaw
---> Using cache
---> d86c9fec99fb
Step 7/56 : RUN npm ci && npm run build
---> Using cache
---> edec71142c73
Step 8/56 : FROM ${BASE_IMAGE}
---> b8af8a05df0a
Step 9/56 : RUN apt-mark manual procps 2>/dev/null || true && (apt-get remove --purge -y gcc gcc-12 g++ g++-12 cpp cpp-12 make netcat-openbsd netcat-traditional ncat 2>/dev/null || true) && apt-get autoremove --purge -y && if ! command -v ps >/dev/null 2>&1; then apt-get update && apt-get install -y --no-install-recommends procps=2:4.0.2-3 && rm -rf /var/lib/apt/lists/*; else rm -rf /var/lib/apt/lists/*; fi && ps --version
---> Running in 39da23d8ed61
procps was already set to manually installed.
Reading package lists...
Building dependency tree...
Reading state information...
Package 'make' is not installed, so not removed
Reading package lists...
Building dependency tree...
Reading state information...
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
ps from procps-ng 4.0.4
---> Removed intermediate container 39da23d8ed61
---> 929891c61903
Step 10/56 : COPY --from=builder /opt/nemoclaw/dist/ /opt/nemoclaw/dist/
---> 9ce727869c72
Step 11/56 : COPY nemoclaw/openclaw.plugin.json /opt/nemoclaw/
---> 6b869c2a2acd
Step 12/56 : COPY nemoclaw/package.json nemoclaw/package-lock.json /opt/nemoclaw/
---> 01144433dbd5
Step 13/56 : COPY nemoclaw-blueprint/ /opt/nemoclaw-blueprint/
---> 12afe4ebc02e
Step 14/56 : WORKDIR /opt/nemoclaw
---> Running in 9d9cef419314
---> Removed intermediate container 9d9cef419314
---> 8849f360935c
Step 15/56 : RUN npm ci --omit=dev
---> Running in fe2f3ad5ebe1
added 32 packages, and audited 33 packages in 589ms
16 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
---> Removed intermediate container fe2f3ad5ebe1
---> a61d33e0ba72
Step 16/56 : RUN set -eu; MIN_VER=$(grep -m 1 'min_openclaw_version' /opt/nemoclaw-blueprint/blueprint.yaml | awk '{print $2}' | tr -d '"'); [ -n "$MIN_VER" ] || { echo "ERROR: Could not parse min_openclaw_version from blueprint.yaml" >&2; exit 1; }; CUR_VER=$(openclaw --version 2>/dev/null | awk '{print $2}' || echo "0.0.0"); if [ "$(printf '%s\n%s' "$MIN_VER" "$CUR_VER" | sort -V | head -n1)" = "$MIN_VER" ]; then echo "INFO: OpenClaw $CUR_VER is current (>= $MIN_VER), no upgrade needed"; else echo "INFO: Base image has OpenClaw $CUR_VER, upgrading to $MIN_VER (minimum required)"; rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw; npm install -g --no-audit --no-fund --no-progress "openclaw@${MIN_VER}"; fi
---> Running in 8ef5d78d580e
INFO: OpenClaw 2026.4.24 is current (>= 2026.4.9), no upgrade needed
---> Removed intermediate container 8ef5d78d580e
---> f639e6fb2fbf
Step 17/56 : RUN set -eu; OC_DIST=/usr/local/lib/node_modules/openclaw/dist; fg_export="$(grep -RIlE --include='*.js' 'export \{[^}]*withStrictGuardedFetchMode as [a-z]' "$OC_DIST")"; test -n "$fg_export"; for f in $fg_export; do grep -q 'withTrustedEnvProxyGuardedFetchMode' "$f" || { echo "ERROR: $f missing withTrustedEnvProxyGuardedFetchMode"; exit 1; }; done; printf '%s\n' "$fg_export" | xargs sed -i -E 's|withStrictGuardedFetchMode as ([a-z])|withTrustedEnvProxyGuardedFetchMode as \1|g'; if grep -REq --include='*.js' 'withStrictGuardedFetchMode as [a-z]' "$OC_DIST"; then echo "ERROR: Patch 1 left strict-mode export alias" >&2; exit 1; fi; fg_assert="$(grep -RIlE --include='*.js' 'async function assertExplicitProxyAllowed' "$OC_DIST")"; test -n "$fg_assert"; printf '%s\n' "$fg_assert" | xargs sed -i -E 's|(async function assertExplicitProxyAllowed\([^)]*\) \{)|\1 if (process.env.OPENSHELL_SANDBOX === "1") return; /* nemoclaw: env-gated bypass, see Dockerfile */ |'; grep -REq --include='*.js' 'assertExplicitProxyAllowed\([^)]*\) \{ if \(process\.env\.OPENSHELL_SANDBOX === "1"\) return; /\* nemoclaw' "$OC_DIST"; isp_file="$(grep -RIlE --include='*.js' 'const baseLstat = await fs\.lstat\(baseDir\)' "$OC_DIST/install-safe-path-"*.js)"; test -n "$isp_file" || { echo "ERROR: install-safe-path baseLstat pattern not found" >&2; exit 1; }; sed -i 's/const baseLstat = await fs\.lstat(baseDir)/const baseLstat = await fs.stat(baseDir)/' "$isp_file"; if grep -q 'const baseLstat = await fs\.lstat(baseDir)' "$isp_file"; then echo "ERROR: Patch 3a (install-safe-path) left baseLstat lstat call" >&2; exit 1; fi; ipd_file="$(grep -RIlE --include='*.js' 'assertInstallBaseStable' "$OC_DIST/install-package-dir-"*.js)"; test -n "$ipd_file" || { echo "ERROR: install-package-dir assertInstallBaseStable not found" >&2; exit 1; }; sed -i 's/const baseLstat = await fs\.lstat(params\.installBaseDir)/const baseLstat = await fs.stat(params.installBaseDir)/' "$ipd_file"; sed -i 's/baseLstat\.isSymbolicLink()/false \/* nemoclaw: symlink check disabled, realpath guards containment *\//' "$ipd_file"; if grep -q 'fs\.lstat(params\.installBaseDir)' "$ipd_file"; then echo "ERROR: Patch 3b (install-package-dir) left lstat in assertInstallBaseStable" >&2; exit 1; fi; rcf_file="$(grep -RIlE --include='*.js' 'async function replaceConfigFile\(params\)' "$OC_DIST" | head -n 1)"; test -n "$rcf_file" || { echo "ERROR: replaceConfigFile function not found in OpenClaw dist" >&2; exit 1; }; python3 -c "import sys; p=sys.argv[1]; f=open(p); src=f.read(); f.close(); old='\tawait writeConfigFile(params.nextConfig, {\n\t\t...writeOptions,\n\t\t...params.writeOptions\n\t});'; new='\ttry { await writeConfigFile(params.nextConfig, {\n\t\t...writeOptions,\n\t\t...params.writeOptions\n\t}); } catch(_rcfErr) { if (process.env.OPENSHELL_SANDBOX === \"1\" && _rcfErr.code === \"EACCES\") { console.error(\"[nemoclaw] Config is read-only in sandbox \\u2014 plugin metadata not persisted (plugins auto-load from extensions/)\"); } else { throw _rcfErr; } }'; assert old in src, 'writeConfigFile(params.nextConfig) pattern not found'; f=open(p,'w'); f.write(src.replace(old,new,1)); f.close()" "$rcf_file"; grep -REq --include='*.js' 'OPENSHELL_SANDBOX.*EACCES' "$rcf_file" || { echo "ERROR: Patch 4 (replaceConfigFile EACCES) not applied" >&2; exit 1; }
---> Running in db5efcc3cfb7
Traceback (most recent call last):
File "<string>", line 1, in <module>
import sys; p=sys.argv[1]; f=open(p); src=f.read(); f.close(); old='\tawait writeConfigFile(params.nextConfig, {\n\t\t...writeOptions,\n\t\t...params.writeOptions\n\t});'; new='\ttry { await writeConfigFile(params.nextConfig, {\n\t\t...writeOptions,\n\t\t...params.writeOptions\n\t}); } catch(_rcfErr) { if (process.env.OPENSHELL_SANDBOX === "1" && _rcfErr.code === "EACCES") { console.error("[nemoclaw] Config is read-only in sandbox \u2014 plugin metadata not persisted (plugins auto-load from extensions/)"); } else { throw _rcfErr; } }'; assert old in src, 'writeConfigFile(params.nextConfig) pattern not found'; f=open(p,'w'); f.write(src.replace(old,new,1)); f.close()
^^^^^^^^^^
AssertionError: writeConfigFile(params.nextConfig) pattern not found
Error: × Docker build stream error
╰─▶ Docker stream error: The command '/bin/bash -o pipefail -c set -eu;
OC_DIST=/usr/local/lib/node_modules/openclaw/dist; fg_export="$(grep
-RIlE --include='*.js' 'export \{[^}]*withStrictGuardedFetchMode as [a-
z]' "$OC_DIST")"; test -n "$fg_export"; for f in $fg_export; do
grep -q 'withTrustedEnvProxyGuardedFetchMode' "$f" || { echo "ERROR: $f
missing withTrustedEnvProxyGuardedFetchMode"; exit 1; }; done;
[... full patch script ...]
"ERROR: Patch 4 (replaceConfigFile EACCES) not applied" >&2; exit 1; }'
returned a non-zero code: 1
Logs
Checklist
Description
Summary:
Sandbox image build fails deterministically at Step 17/56 on Apple Silicon macOS. Patch 4 in Step 17 attempts to wrap a specific source pattern in OpenClaw's
replaceConfigFile, but that pattern does not exist in the bundled OpenClaw 2026.4.24. The build aborts before sandbox creation completes.This blocks all Apple Silicon macOS users from creating a new sandbox via nemoclaw onboard, including fresh installs and rebuilds of previously-working sandboxes.
Reproduction Steps
Steps to Reproduce
curl -fsSL https://www.nvidia.com/nemoclaw.sh | bashnemotron-3-nano:30bas the model.Expected Behavior:
Step 17/56 applies all four patches successfully. Build proceeds to Step 18 and completes. Sandbox is registered in the live OpenShell gateway.
Actual Behavior:
Step 17/56 fails at Patch 4 with:
Build exits 1. Onboard reports
Sandbox creation failed (exit 1). Subsequentnemoclaw test connectreportsSandbox 'test' is not present in the live OpenShell gateway. Removed stale local registry entry.Diagnosis
Patches 1, 2, 3a, and 3b in Step 17 apply successfully (no errors raised). Patch 4 fails because the source pattern it searches for:
does not exist in OpenClaw 2026.4.24's
replaceConfigFilefunction. OpenClaw appears to have refactored this function before 2026.4.24 was bundled into the current base image, so the patch was effectively broken against this base image on the day it was published.Reproducibility:
100%. Reproduces on every
nemoclaw onboardattempt, including:Initial successful onboard occurred on 2026-04-28 (different base image SHA: 331f52d607f4, same bundled OpenClaw version 2026.4.24 per build log). Same failure observed on 2026-05-01 and again on 2026-05-13 against the current base image SHA b8af8a05df0a. The regression appears to have landed in NemoClaw's patch script or the base image rebuild process between April 28 and May 1, and has not been corrected since.
Suggested Fix Directions:
Environment
Environment:
Debug Output
Logs
Checklist