Skip to content

fix: exec workdir with leading ~ falls back and still executes command#94447

Closed
sunlit-deng wants to merge 1 commit into
openclaw:mainfrom
sunlit-deng:fix/issue-94434
Closed

fix: exec workdir with leading ~ falls back and still executes command#94447
sunlit-deng wants to merge 1 commit into
openclaw:mainfrom
sunlit-deng:fix/issue-94434

Conversation

@sunlit-deng

Copy link
Copy Markdown
Contributor

Summary

Fixes #94434

  • exec tool's resolveWorkdir() now expands leading ~ in workdir paths before stat resolution
  • Reuses the existing and well-tested expandHomePrefix() utility from src/infra/home-dir.ts
  • Fallback behavior for genuinely nonexistent paths is fully preserved
  • Minimal change: one import, one line of logic, one variable reference update

Linked context

Real behavior proof (required for external PRs)

Behavior addressed: resolveWorkdir() now expands leading ~ (tilde) in the workdir argument via the existing expandHomePrefix() utility before calling statSync(). Previously, a literal ~ was passed to statSync(), which Node.js does not expand, causing ENOENT and a silent fallback to the current working directory. Relative commands like ./scripts/run.py would then execute from the wrong directory.

Real setup tested:

  • Runtime: node v24.13.1 (via node --import tsx)
  • Gateway: N/A — fix targets the function directly, no gateway interaction needed
  • Worktree: /home/0668000974/workspace/openclaw-worktrees/issue-94434
  • Home directory: /home/0668000974

Exact steps or command run after fix:

cd /home/0668000974/workspace/openclaw-worktrees/issue-94434
export OPENCLAW_STATE_DIR=$(mktemp -d)
node --import tsx -e "
import { expandHomePrefix } from './src/infra/home-dir.js';
import { resolveWorkdir } from './src/agents/bash-tools.shared.js';
import os from 'node:os';

const home = os.homedir();
console.log('=== expandHomePrefix verification ===');
console.log('Input: ~/workspace');
console.log('Output:', expandHomePrefix('~/workspace'));
console.log('Home dir:', home);

console.log();
console.log('=== resolveWorkdir with valid tilde path ===');
const w1 = [];
console.log('Input: ~/');
console.log('Output:', resolveWorkdir('~/', w1));
console.log('Warnings:', JSON.stringify(w1));

console.log();
console.log('=== resolveWorkdir with invalid tilde path (fallback preserved) ===');
const w2 = [];
console.log('Input: ~/nonexistent_dir_xyz123');
console.log('Output:', resolveWorkdir('~/nonexistent_dir_xyz123', w2));
console.log('Warnings:', JSON.stringify(w2));

console.log();
console.log('=== resolveWorkdir with literal path (no regression) ===');
const w3 = [];
console.log('Input: /tmp');
console.log('Output:', resolveWorkdir('/tmp', w3));
console.log('Warnings:', JSON.stringify(w3));
"

After-fix evidence:

=== expandHomePrefix verification ===
Input: ~/workspace
Output: /home/0668000974/workspace
Home dir: /home/0668000974

=== resolveWorkdir with valid tilde path ===
Input: ~/
Output: /home/0668000974/
Warnings: []

=== resolveWorkdir with invalid tilde path (fallback preserved) ===
Input: ~/nonexistent_dir_xyz123
Output: /home/0668000974/workspace/openclaw-worktrees/issue-94434
Warnings: ["Warning: workdir \"~/nonexistent_dir_xyz123\" is unavailable; using \"/home/0668000974/workspace/openclaw-worktrees/issue-94434\"."]

=== resolveWorkdir with literal path (no regression) ===
Input: /tmp
Output: /tmp
Warnings: []

Observed result after the fix: ~/ resolves to /home/0668000974/ with no warnings (previously would have silently fallen back to CWD). ~/nonexistent_dir_xyz123 triggers the fallback to CWD with a warning message, preserving the existing safety behavior. /tmp (non-tilde path) works identically to before — no regression. Relative commands like ./scripts/run.py will now execute from the correct expanded directory instead of the fallback directory.

What was not tested: Full gateway end-to-end with live model emitting workdir: "~..." tool calls — blocked by a pre-existing @pierre/diffs build dependency issue in this worktree that is unrelated to this change. The function-level verification above directly tests the changed code path. Sandbox workdir path (uses separate resolver, not affected).

Proof limitations or environment constraints: The @pierre/diffs build dependency is missing in this worktree, preventing a full pnpm openclaw exec test. However, the fix is purely in resolveWorkdir() which is tested directly above. The existing expandHomePrefix() utility has 27 passing unit tests in src/infra/home-dir.test.ts.

Tests and validation

  • src/infra/home-dir.test.ts: 27/27 tests passed — expandHomePrefix is already well-covered
  • TypeScript typecheck on modified file: No errors found
  • pnpm check: All guards passed; only pre-existing npm shrinkwrap guard failure (unrelated)
  • Manual function verification: 4 scenarios tested (valid tilde, invalid tilde + fallback, literal path, expandHomePrefix utility) — all produce correct results

How I know the risk surface is small

The change introduces no new algorithms, dependencies, or file I/O. It calls an existing utility that is already in production use (via executable-path.ts for the same purpose — expanding ~ in user-supplied paths). The edit touches 3 lines in a single file:

  1. Import expandHomePrefix from a sibling module
  2. const expanded = expandHomePrefix(workdir) — one new line
  3. statSync(expanded) instead of statSync(workdir) — one variable reference change
  4. Return expanded instead of workdir — correct, consumers should receive the resolved path

Paths that do not start with ~ pass through expandHomePrefix() unchanged (identity return on line 99 of home-dir.ts). The fallback path is unmodified.

Boundary cases

  • Full shell expansion? No. expandHomePrefix only matches leading ~ followed by end-of-string or path separator. $HOME, ${HOME}, wildcards, etc. are not expanded. This is consistent with the issue reporter's request.
  • ~user/other? No. The regex /^~(?=$|[\\/])/ only matches bare ~. Structured tool arguments should not perform user-lookup expansion.
  • Sandbox? Sandbox codepaths use their own resolveWorkdir override (see sandbox/backend.test.ts:49), not this function. The change is scoped to host-side exec.
  • OPENCLAW_HOME? expandHomePrefix honors OPENCLAW_HOME env var, consistent with all other path resolution in the codebase.
  • Symlinks? statSync resolves real paths through the OS as before, no change.

Risk checklist

Did user-visible behavior change? (Yes / No)
Yes — exec calls with workdir: "~..." now run from the expanded directory instead of silently falling back to CWD.

Did config, environment, or migration behavior change? (Yes / No)
No — no new config keys, no migration scripts, no environment variable changes.

Did security, auth, secrets, network, or tool execution behavior change? (Yes / No)
No — only local path expansion before stat check. No network, auth, or tool execution changes.

What is the highest-risk area?

  • An exec call chain that previously relied on the silent-fallback-to-CWD behavior when using ~ paths will now execute from the expanded directory. This is the intended fix, but it is a behavior change.

How is that risk mitigated?

  • The fallback is fully preserved for genuinely nonexistent directories. Only paths that map to real, existing directories after ~ expansion will behave differently. This is universally an improvement — the prior behavior was a bug, not a feature.

Current review state

What is the next action?

  • Maintainer review

What is still waiting on author, maintainer, CI, or external proof?

  • Nothing — fix is complete with direct function-level verification. No pending TODO items.

Which bot or reviewer comments were addressed?

  • Not applicable — this is a new PR. ClawSweeper has started review on the issue itself but has not yet posted findings.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling proof: supplied External PR includes structured after-fix real behavior proof. size: XS labels Jun 18, 2026
@clawsweeper

clawsweeper Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close: a viable sibling PR now owns the same exec workdir bug with the safer OS-home semantics and regression coverage, while this branch still has the earlier OPENCLAW_HOME compatibility problem.

Root-cause cluster
Relationship: superseded
Canonical: #94449
Summary: This PR and the canonical sibling PR target the same exec workdir tilde bug; the sibling PR now contains the safer OS-home implementation and regression coverage.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Close this duplicate PR and use #94449 as the canonical landing path for #94434.

So I’m closing this here and keeping the remaining discussion on #94449 and #94434.

Review details

Best possible solution:

Close this duplicate PR and use #94449 as the canonical landing path for #94434.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows local gateway exec reaches resolveWorkdir(), and current main plus v2026.6.8 stat the literal ~/... path before falling back; I did not execute a live command in this read-only review.

Is this the best way to solve the issue?

No. This branch is not the best remaining landing path because it uses the effective OpenClaw home, while #94449 already applies OS-home semantics with regression coverage.

Security review:

Security review cleared: No supply-chain, permissions, secrets, or security-boundary regression was found; the blocker is functional cwd compatibility and duplicate landing path selection.

AGENTS.md: found and applied where relevant.

What I checked:

  • Repository policy read: Root and scoped agent AGENTS.md files were read fully; the root policy treats fallback behavior and agent-runtime compatibility as merge-sensitive, so the review compared sibling workdir semantics instead of diff-only approval. (AGENTS.md:7, b073d7cc11dc)
  • Current PR changes the host workdir resolver: The PR calls expandHomePrefix(workdir) in resolveWorkdir() before statSync() and returns the expanded path on success. (src/agents/bash-tools.shared.ts:184, ee3cee7213c1)
  • Current main still has the original literal stat behavior: Current main still stats the raw workdir and falls back with a warning, so the linked bug is not implemented on main yet. (src/agents/bash-tools.shared.ts:183, b073d7cc11dc)
  • Latest release still has the same behavior: Release v2026.6.8 also stats the raw workdir, confirming this PR is not obsolete because the bug shipped unchanged. (src/agents/bash-tools.shared.ts:183, 844f405ac1be)
  • Helper default uses OpenClaw effective home: expandHomePrefix() defaults through resolveEffectiveHomeDir(), which honors OPENCLAW_HOME; that is the compatibility problem in this branch. (src/infra/home-dir.ts:89, b073d7cc11dc)
  • Sibling host file tools use OS-home semantics: Host file path resolution passes resolveOsHomeDir() into expandHomePrefix(), and adjacent tests assert OPENCLAW_HOME is ignored for host ~ file paths. (src/agents/agent-tools.read.ts:968, b073d7cc11dc)

Likely related people:

  • vincentkoc: Authored the sibling PR's maintainer refactor that applies OS-home expansion and adds divergent-home coverage for this exact resolver. (role: recent canonical fix owner; confidence: high; commits: 710e0eff9d98; files: src/agents/bash-tools.shared.ts, src/agents/bash-tools.shared.test.ts)
  • steipete: Live commit metadata shows the shared bash tool module and original resolver were introduced in the agent tool split commit. (role: introduced behavior; confidence: medium; commits: e2f89099829c; files: src/agents/bash-tools.shared.ts, src/agents/bash-tools.exec.ts)
  • openperf: A prior merged exec change adjusted remote-node cwd handling around the same bash-tools.exec.ts workdir routing surface. (role: adjacent exec workdir contributor; confidence: medium; commits: fc3f6fa51f98; files: src/agents/bash-tools.exec.ts, src/agents/bash-tools.exec.approval-id.test.ts)
  • Vincent Koc: Local blame in this shallow checkout attributes the current resolveWorkdir() fallback block to a recent grafted commit, so this is useful as a routing hint but not sole ownership proof. (role: current implementation provenance; confidence: low; commits: fb06df6cadf1; files: src/agents/bash-tools.shared.ts)

Codex review notes: model internal, reasoning high; reviewed against b073d7cc11dc.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 18, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 19, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 19, 2026
@clawsweeper clawsweeper Bot added status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 19, 2026
@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: exec workdir with leading ~ falls back and still executes command

1 participant