openclaw - ✅(Solved) Fix [Bug]: Workspace .py scripts break on v2026.4.x — gateway image (node:24-bookworm-slim base) ships no python3 [6 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#75041Fetched 2026-05-01 05:38:51
View on GitHub
Comments
1
Participants
2
Timeline
10
Reactions
2
Assignees
Timeline (top)
cross-referenced ×7assigned ×1closed ×1commented ×1

The gateway image ghcr.io/openclaw/openclaw:2026.4.27 (and at least 2026.4.25/26 — confirmed for .27, regression vs 2026.4.23) ships no python3 interpreter. User-authored workspace scripts that shebang python3 therefore fail at runtime with python3: not found, and the agent surfaces the failure to the user as e.g. "Unable to fetch — astronomy script needs python3 (not installed on this system)."

This is the same root cause already discussed in #72362 / #73791 (slim base image lacks python3), but those issues / PRs only address the internal fs-safe helper path. Fixing the internal helper fallback does not restore the ability to run user-authored .py scripts in workspace, which is a separate use case worth tracking.

Error Message

  1. Make the symptom non-silent. When an agent invokes a .py workspace script and python3 isn't on PATH, the error returned to the agent should explicitly say "python3 is not installed in this image — see https://… for how to add it" rather than letting the agent paraphrase it however it wants.

Root Cause

This is the same root cause already discussed in #72362 / #73791 (slim base image lacks python3), but those issues / PRs only address the internal fs-safe helper path. Fixing the internal helper fallback does not restore the ability to run user-authored .py scripts in workspace, which is a separate use case worth tracking.

Fix Action

Fix / Workaround

Local workaround

PR fix notes

PR #71235: Dockerfile: add python -> python3 symlink for bookworm

Description (problem / solution / changelog)

Summary

Add a conditional pythonpython3 symlink to every Debian-based OpenClaw Dockerfile so agents that default to python (rather than python3) don't waste a tool round trip on command not found.

Problem

Debian bookworm dropped the unversioned python command. Every OpenClaw image that ships python3 — whether via the base image, the OPENCLAW_DOCKER_APT_PACKAGES apt list, or a per-solution extension Dockerfile — inherits the missing symlink.

LLM agents pretrained on a corpus where python is the canonical invocation reach for python first. Inside an OpenClaw container, that's:

  1. Tool call: python some-script.py
  2. Response: /bin/sh: python: command not found
  3. Reasoning + retry: python3 some-script.py

Roughly two extra tool turns and a few hundred wasted tokens of conversation churn per attempt. Across every Python-using session, for every OpenClaw user, on every bookworm-based image, this is a steady, measurable, free-to-fix token tax. The underlying debian-conformance issue is upstream's; what this PR addresses is the gap between agent behaviour priors and image reality.

Fix

All four Dockerfiles (Dockerfile, Dockerfile.sandbox, Dockerfile.sandbox-browser, Dockerfile.sandbox-common) get the same conditional pattern:

RUN if command -v python3 >/dev/null 2>&1 && [ ! -e /usr/local/bin/python ]; then \
      ln -sf /usr/bin/python3 /usr/local/bin/python; \
    fi
  • No-op for slim variants that don't ship python3.
  • No-op if an operator has installed python-is-python3 (the Debian-blessed alternative) — their symlink wins.
  • No-op on re-build (idempotent).

Why not python-is-python3?

Reasonable question. The apt package is the dpkg-blessed answer and would work for most images. I went with the conditional symlink instead because:

  1. Slim variants may not have python3 at all. python-is-python3 would fail an apt install there; the conditional symlink gracefully no-ops.
  2. Gateway's optional apt layer — the main Dockerfile's apt-install step is already conditional on OPENCLAW_DOCKER_APT_PACKAGES being set. Adding python-is-python3 as a default apt dependency would force an apt round-trip that isn't there today.
  3. Base-image portability — the symlink approach works if the base image is ever swapped to a non-Debian distribution (Alpine, UBI, etc.). The package is Debian-only.

If maintainers prefer the package on the gateway image specifically, happy to split that out — the sandbox Dockerfiles would still want the conditional symlink pattern for the portability reason.

Test plan

  • Rebuild openclaw:default locally; verify python --version succeeds inside a running gateway container where it previously errored.
  • Rebuild a slim variant without python3; verify the new layer is a no-op and the image still builds.
  • Existing agent workflows that reach for python stop burning tool round trips.

AI disclosure

AI-assisted fix, lightly tested (manual rebuild + smoke test across variants; no automated test coverage since the repo doesn't run Dockerfile-level tests). I understand what the code does.

Changed files

  • Dockerfile (modified, +8/-0)
  • Dockerfile.sandbox (modified, +8/-0)
  • Dockerfile.sandbox-browser (modified, +8/-0)
  • Dockerfile.sandbox-common (modified, +8/-0)

PR #73791: fix(infra/fs-safe): fall back to legacy write/copy when python3 helper missing (#72362)

Description (problem / solution / changelog)

Summary

Fixes #72362. On Docker base images that do not ship python3 (notably node:*-slim), the pinned-path/pinned-write helpers cannot spawn and writeFileWithinRoot/copyFileWithinRoot were swallowing the underlying spawn python3 ENOENT and surfacing the misleading path is not a regular file under root error. That made several user-visible features fail:

  • The bundled session-memory hook never wrote summaries to memory/ on /new or /reset.
  • Agents with tools.fs.workspaceOnly: true couldn't edit, write, or apply_patch on files clearly inside their workspace.
  • Skill installers and any other writeFileWithinRoot consumer hit the same wall.

removePathWithinRoot and mkdirPathWithinRoot already handle this exact case via isPinnedPathHelperSpawnError(error) → legacy fallback. This PR mirrors that pattern for the write/copy paths.

Changes

  • src/infra/fs-safe.ts:
    • writeFileWithinRoot: catch runPinnedWriteHelper errors; on isPinnedPathHelperSpawnError, fall back to writeFileWithinRootLegacy (the same legacy atomic-write path used on Windows). Other errors still go through normalizePinnedWriteError.
    • copyFileWithinRoot: same treatment. The pinned helper consumes the source ReadStream during pipeline cleanup even when spawn fails, so the fallback re-opens the source via openVerifiedLocalFile (preserving the original hardlink-rejection mode) before handing it to copyFileWithinRootLegacy.
  • src/infra/fs-safe.test.ts:
    • 3 new cases pinned to non-Windows: write fallback writes the file, copy fallback copies the file, and non-spawn errors still surface as invalid-path (no fallback masking).
    • afterEach now calls vi.restoreAllMocks() so module-level vi.spyOn mocks don't leak into later tests in the same file.
  • CHANGELOG.md: single-line entry under ## Unreleased > ### Fixes.

Why scope to fallback (not error-message rewording)

Just rewording the error to mention python3 would still leave the user without a working write path. The legacy code already exists, is exercised on Windows, and produces correct atomic writes via fs.rename. Falling back is strictly more useful and matches the prior remove/mkdir precedent.

Test plan

  • pnpm test src/infra/fs-safe.test.ts — 37/37 pass (was 34, +3 new).
  • pnpm tsgo:core clean.
  • pnpm run lint:core clean.
  • pnpm exec oxfmt --check clean.
  • Manual: build a node:24-bookworm-slim based image without installing python3, run openclaw gateway, send a message, /new~/.openclaw/workspace/memory/ should now contain the session summary instead of seeing [hooks/session-memory] Failed to save session memory.

Suggested CHANGELOG entry

This PR intentionally does not touch CHANGELOG.md to avoid hot-file rebase conflicts that have been closing rebased fix PRs. Maintainers can drop the following line under ## Unreleased > ### Fixes at merge time:

  • Infra/fs-safe: when the pinned write helper cannot spawn python3 (e.g. on node:*-slim Docker base images), writeFileWithinRoot and copyFileWithinRoot now fall back to the legacy atomic-write path the same way removePathWithinRoot/mkdirPathWithinRoot already did, so session-memory hooks, workspace-only tools.fs writes, and skill installs land on disk instead of failing with the misleading path is not a regular file under root error. Fixes #72362. Thanks @juan-flores077.

Changed files

  • src/infra/fs-safe.test.ts (modified, +70/-0)
  • src/infra/fs-safe.ts (modified, +56/-25)

PR #75413: docs(docker): document python3 runtime baseline

Description (problem / solution / changelog)

Problem

#75417 restored python3 in the gateway runtime image and closed #75041, but the Docker install docs and Dockerfile build-arg example still describe OPENCLAW_DOCKER_APT_PACKAGES like Python is an extra package to bake into local images. That leaves users reading the docs with the wrong mental model: official release images now include python3; the build arg is only for packages beyond that baseline.

Root cause

The runtime package fix landed in Dockerfile, src/dockerfile.test.ts, and CHANGELOG.md, but the user-visible Docker install page was not updated. The Dockerfile example also still used python3 wget, which made python3 look optional even after it became part of the default runtime package list.

Complete fix boundary

  • Update the Dockerfile runtime comment to call the package list a baseline and explain why python3 is included.
  • Change the optional package example from python3 wget to non-baseline extras.
  • Update docs/install/docker.md so the environment-variable table and power-user example describe OPENCLAW_DOCKER_APT_PACKAGES as local-build extras.
  • Document that official OpenClaw Docker release images include python3 for workspace Python scripts.
  • Add a src/dockerfile.test.ts docs assertion for the python3 baseline wording so the user-visible docs do not drift again.

What intentionally did not change

  • Did not re-add the core python3 runtime package change; #75417 already merged that.
  • Did not add another changelog entry; #75417 already added the release note that fixes #75041.
  • Did not change scripts/docker/setup.sh; it already passes local build extras through only when building openclaw:local.
  • Did not touch sandbox docs or sandbox Dockerfiles; those are a separate execution boundary.

Tests run

  • pnpm test src/dockerfile.test.ts (15 tests passed)
  • pnpm exec oxfmt --check --threads=1 Dockerfile src/dockerfile.test.ts docs/install/docker.md
  • git diff --check upstream/main..HEAD
  • pnpm check:changed

Docker smoke attempted but could not start locally because the Docker daemon is unavailable on this machine:

ERROR: error during connect: Head http://%2F%2F.%2Fpipe%2FdockerDesktopLinuxEngine/_ping: open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

Linked issue

Refs #75041. Follow-up to #75417.

CI red analysis

After rebasing onto latest upstream/main, the previous file conflict in src/dockerfile.test.ts is resolved. New exact-head GitHub checks are running after the force-with-lease update. The local Docker smoke failure is unrelated to this diff because Docker fails before reading the Dockerfile; the Docker Desktop Linux engine pipe is missing locally.

Changed files

  • Dockerfile (modified, +4/-3)
  • docs/install/docker.md (modified, +6/-2)
  • src/dockerfile.test.ts (modified, +12/-0)

PR #75417: fix(docker): restore python3 in runtime image

Description (problem / solution / changelog)

Summary

  • restore python3 in the slim gateway runtime image
  • add Dockerfile coverage for the runtime package list
  • add changelog entry

Fixes #75041 Related: #72362

Testing

  • pnpm vitest run src/dockerfile.test.ts

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • Dockerfile (modified, +1/-1)
  • src/dockerfile.test.ts (modified, +16/-1)

PR #72635: fix(fs-safe): fallback when pinned write helper cannot spawn

Description (problem / solution / changelog)

Summary

Fixes #72362 by making pinned writes continue to work when python3 is missing from PATH in slim Docker images.

Instead of surfacing the misleading generic safe-open error, runPinnedWriteHelper() now detects spawn failures for the Python helper (ENOENT, EACCES, ENOEXEC) and falls back to the existing local writer before any input stream is consumed.

Why this approach

There is already a narrower PR (#72531) that improves the error message. This PR fixes the underlying Docker behavior for write paths: agents using tools.fs.workspaceOnly: true can still write/edit/apply patches even when the image does not include Python.

The fallback is intentionally limited to helper spawn failures. If Python starts and reports a real path/symlink/boundary error, that error still flows through the existing pinned-helper path.

Testing

  • pnpm exec vitest run src/infra/fs-pinned-write-helper.test.ts src/infra/fs-pinned-write-helper.spawn-fallback.test.ts src/infra/fs-safe.test.ts
  • pnpm exec oxfmt --check src/infra/fs-pinned-write-helper.ts src/infra/fs-pinned-write-helper.spawn-fallback.test.ts

Also attempted full typecheck:

  • NODE_OPTIONS=--max-old-space-size=8192 pnpm exec tsc -p tsconfig.json --noEmit

That fails on existing unrelated test typing errors in:

  • src/agents/pi-embedded-runner.sanitize-session-history.test.ts(986,13)
  • src/agents/pi-embedded-runner/run/attempt.test.ts(91,20)

Changed files

  • src/infra/fs-pinned-write-helper.spawn-fallback.test.ts (added, +82/-0)
  • src/infra/fs-pinned-write-helper.ts (modified, +32/-2)

PR #72531: fix(fs-safe): surface clear error when python3 is missing

Description (problem / solution / changelog)

Summary

Fixes #72362 — writeFileWithinRoot silently fails with misleading path is not a regular file under root when python3 is not installed.

Root cause

normalizePinnedWriteError() wraps all errors from the pinned write helper into a generic SafeOpenError('invalid-path', 'path is not a regular file under root'). When the real cause is spawn python3 ENOENT (missing python3 binary), the error message leads users down a dead-end investigation of paths, permissions, and symlinks.

Fix

Added detection in normalizePinnedWriteError() for ENOENT errors related to python3. When detected, surfaces a clear actionable message:

pinned write failed: python3 is not installed. Install python3 (e.g. `apt-get install python3`) or use a base image that includes it.

Checks both the direct error and error.cause since the ENOENT can appear at either level depending on the spawn path.

Changes

  • src/infra/fs-safe.ts: Enhanced normalizePinnedWriteError() to detect python3 ENOENT

Testing

  • TypeScript compilation passes
  • Existing fs-safe.test.ts tests for python3 ENOENT scenarios continue to pass

Changed files

  • src/infra/fs-safe.test.ts (modified, +69/-0)
  • src/infra/fs-safe.ts (modified, +37/-1)

Code Example

docker run --rm --entrypoint sh ghcr.io/openclaw/openclaw:2026.4.27 -c 'which python3 || echo MISSING'
# → MISSING

docker run --rm --entrypoint sh ghcr.io/openclaw/openclaw:2026.4.23 -c 'which python3'
# → /usr/bin/python3   (3.11.2)

---

FROM ghcr.io/openclaw/openclaw:2026.4.27
USER root
RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 \
 && rm -rf /var/lib/apt/lists/*
USER node
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

The gateway image ghcr.io/openclaw/openclaw:2026.4.27 (and at least 2026.4.25/26 — confirmed for .27, regression vs 2026.4.23) ships no python3 interpreter. User-authored workspace scripts that shebang python3 therefore fail at runtime with python3: not found, and the agent surfaces the failure to the user as e.g. "Unable to fetch — astronomy script needs python3 (not installed on this system)."

This is the same root cause already discussed in #72362 / #73791 (slim base image lacks python3), but those issues / PRs only address the internal fs-safe helper path. Fixing the internal helper fallback does not restore the ability to run user-authored .py scripts in workspace, which is a separate use case worth tracking.

Reproduce

docker run --rm --entrypoint sh ghcr.io/openclaw/openclaw:2026.4.27 -c 'which python3 || echo MISSING'
# → MISSING

docker run --rm --entrypoint sh ghcr.io/openclaw/openclaw:2026.4.23 -c 'which python3'
# → /usr/bin/python3   (3.11.2)

Then any workspace skill that runs a .py script — e.g. a tonight-sky / astronomy skill that calls python3 scripts/astronomy.py — fails on 2026.4.27 but worked on 2026.4.23.

Why this matters beyond fs-safe (#72362)

#73791 adds a legacy JS fallback for the internal writeFileWithinRoot / copyFileWithinRoot helpers, which is great for fs-safe correctness. But it does nothing for:

  • User-authored Python skill scripts in workspace/scripts/.
  • Bundled-extension skills that ship Python (e.g. matplotlib-charting, ephem-based astronomy, anything pandas/numpy).
  • Anyone whose deployment relies on python being usable inside the gateway container at all.

The current behavior on a slim image is that these all silently break on upgrade with no migration note.

Suggested fixes (not mutually exclusive)

  1. Ship python3 in the gateway image again. Even if fs-safe gets a JS fallback via #73791, a baseline python3 (no extras) costs ~30 MB and unbreaks every user-Python skill out there. The release notes for whichever version dropped python3 should also call this out as a breaking change.
  2. If keeping the slim base is a hard requirement, publish a documented OPENCLAW_DOCKER_APT_PACKAGES=python3 (or similar) escape hatch and a short Dockerfile recipe in the docs for re-adding it locally — and emit a one-time deprecation log line on first run when a workspace .py script is invoked without python3.
  3. Make the symptom non-silent. When an agent invokes a .py workspace script and python3 isn't on PATH, the error returned to the agent should explicitly say "python3 is not installed in this image — see https://… for how to add it" rather than letting the agent paraphrase it however it wants.

Local workaround

Layer python3 on top of the official image:

FROM ghcr.io/openclaw/openclaw:2026.4.27
USER root
RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 \
 && rm -rf /var/lib/apt/lists/*
USER node

…then point docker-compose.yml at the locally built tag. Verified to unbreak ephem-based astronomy and matplotlib charting.

Environment

  • Image: ghcr.io/openclaw/openclaw:2026.4.27
  • Base: node:24-bookworm-slim (per #72362)
  • Last known good: ghcr.io/openclaw/openclaw:2026.4.23 (had /usr/bin/python3 3.11.2)

Related

  • #72362 — fs-safe internal helper requires python3 (same root cause, different symptom)
  • #73791 — fix for the fs-safe helper specifically (legacy fallback)
  • #71235 — pythonpython3 symlink (assumes python3 is already installed; orthogonal)

extent analysis

TL;DR

The gateway image is missing a Python 3 interpreter, causing user-authored workspace scripts that rely on python3 to fail.

Guidance

  • The issue can be resolved by shipping python3 in the gateway image again, which would unbreak user-Python skills.
  • As an alternative, a documented escape hatch like OPENCLAW_DOCKER_APT_PACKAGES=python3 can be used to re-add python3 locally.
  • To make the symptom non-silent, the error message can be modified to explicitly state that python3 is not installed in the image.
  • A local workaround is to layer python3 on top of the official image using a custom Dockerfile.

Example

FROM ghcr.io/openclaw/openclaw:2026.4.27
USER root
RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 \
 && rm -rf /var/lib/apt/lists/*
USER node

Notes

The suggested fixes are not mutually exclusive, and a combination of them can be used to address the issue. The local workaround has been verified to unbreak ephem-based astronomy and matplotlib charting.

Recommendation

Apply the local workaround by layering python3 on top of the official image, as it provides a quick and effective solution to the problem. This approach allows users to continue using their existing workspace scripts without modifying the gateway image.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: Workspace .py scripts break on v2026.4.x — gateway image (node:24-bookworm-slim base) ships no python3 [6 pull requests, 1 comments, 2 participants]