Skip to content

fix(shell): verify working directory exists before launching process#1299

Merged
Aaronontheweb merged 2 commits into
devfrom
fix/shell-tool-working-directory-1286
Jun 3, 2026
Merged

fix(shell): verify working directory exists before launching process#1299
Aaronontheweb merged 2 commits into
devfrom
fix/shell-tool-working-directory-1286

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Problem

Closes #1286.

ShellTool and BackgroundJobExecutionActor set ProcessStartInfo.WorkingDirectory without verifying the path exists. Only the session scratch dir was auto-created; any other resolved cwd (explicit arg, project dir, inherited cwd, or a background job's working directory) was passed straight to Process.Start, which throws an opaque, platform-specific error when the directory is missing. Agents couldn't tell why the command failed and retry-looped instead of creating the directory.

Fix

Both process-launch paths now fail loudly before Process.Start with an actionable message:

  • missing dir → Working directory '<path>' does not exist. Create it first, e.g.: mkdir -p "<path>" (platform-aware — no -p on Windows cmd.exe)
  • path is a file → Working directory '<path>' is a file, not a directory.

No silent auto-creation: the cwd is a security-relevant, approval-gated input, so we fail loud per the constitution's "no silent fallbacks" rule. The session scratch dir keeps its existing auto-create behavior.

Why this is loop-free

Approvals are existence-agnostic — saved and matched as normalized path strings (Path.GetFullPath, no Directory.Exists, no TTL). So an approval granted for a not-yet-existent cwd still matches after the agent runs mkdir. The cryptic error was the only thing keeping the agent stuck.

Changes

File Change
src/Netclaw.Actors/Tools/ShellTool.cs Existence guard in the cwd-resolution block (returns the error string).
src/Netclaw.Actors/Jobs/BackgroundJobExecutionActor.cs Same guard before Process.Start, reported via the existing ReportCompletion(Failed, …) idiom.
src/Netclaw.Actors.Tests/Tools/ShellToolTests.cs +3 tests: missing explicit dir (asserts no process start, no silent auto-create), path-is-a-file, missing project dir via fallback chain.
src/Netclaw.Actors.Tests/Jobs/BackgroundJobIntegrationTests.cs +1 end-to-end test: missing working dir → helpful failure delivered + persisted Failed.

Verification

  • ShellToolTests + BackgroundJobIntegrationTests — all green (4 new)
  • dotnet slopwatch analyze — 0 issues
  • Add-FileHeaders.ps1 -Verify — all headers present

Review notes (follow-ups, not blocking)

A multi-angle review found no functional bug in the diff. Minor items left as potential follow-ups:

  • An existing-but-inaccessible (permission-denied) directory reports "does not exist" since Directory.Exists returns false for ACL failures.
  • Pre-existing asymmetry: background jobs with a null WorkingDirectory inherit the daemon cwd, which ShellTool deliberately never does.
  • The guard logic is duplicated across the two sites (candidate for a shared PathUtility helper); the mkdir hint isn't shell-escaped (cosmetic).

ShellTool and BackgroundJobExecutionActor set ProcessStartInfo.WorkingDirectory
without checking that the path exists. Only the session scratch dir was
auto-created; any other resolved cwd (explicit arg, project dir, inherited cwd,
or a background job's working directory) was passed straight to Process.Start,
which throws an opaque, platform-specific error when the directory is missing.
Agents could not tell why the command failed and retry-looped instead of
creating the directory.

Both paths now fail loudly with an actionable message naming the path and the
mkdir remedy (platform-aware: no -p on Windows cmd.exe), and distinguish a
missing directory from a path that is actually a file. No silent auto-creation:
the cwd is a security-relevant, approval-gated input. Approvals are
existence-agnostic, so an approval granted before the directory exists still
matches once the agent creates it, avoiding a re-approval loop.

Closes #1286
"auto-ack-slack-gateway-missing-cwd");
ActorRegistry.For(Sys).Register<SlackGatewayActorKey>(autoAckRef);

var missingDir = Path.Combine(_dir.Path, "does", "not", "exist");
[Fact]
public async Task Missing_explicit_working_directory_returns_helpful_error()
{
var missingDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
[Fact]
public async Task Working_directory_that_is_a_file_returns_not_a_directory_error()
{
var filePath = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
{
// No explicit arg, so the resolution chain falls back to ProjectDirectory —
// proving the existence guard covers the fallback paths, not just explicit args.
var sessionDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
// No explicit arg, so the resolution chain falls back to ProjectDirectory —
// proving the existence guard covers the fallback paths, not just explicit args.
var sessionDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
var missingProjectDir = Path.GetFullPath(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")));
@Aaronontheweb Aaronontheweb added bug Something isn't working tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc. labels Jun 3, 2026
@Aaronontheweb Aaronontheweb marked this pull request as ready for review June 3, 2026 04:08

@Aaronontheweb Aaronontheweb left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb Aaronontheweb merged commit 05f88e1 into dev Jun 3, 2026
20 checks passed
@Aaronontheweb Aaronontheweb deleted the fix/shell-tool-working-directory-1286 branch June 3, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working tools Issues related to agent tools: file_read, web_search, shell_execute, image processing, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: ShellTool sets ProcessStartInfo.WorkingDirectory without verifying directory exists

1 participant