Skip to content

Delegated claude-cli agent nodes fail with an empty error message #96

Description

@Shashankss1205

Found while benchmarking (bench/). On the claude-cli/* backend, stdlib agent nodes (investigate, summarize) fail in ~2s and fold an empty exception message into state: findings=['[error] ']. A bare claude -p works on the same machine and same prompt, so the defect is somewhere in the adapter's hardened invocation (isolated settings, disallowed tools, stdin prompt), not in the CLI itself. Reproduced with all CLAUDE* env vars stripped, so it is not (only) nested-session detection.

Two problems, the second worse than the first:

  1. The delegated call fails where a direct one succeeds — needs the actual stderr/exception surfaced; an empty string is undebuggable.
  2. The stdlib goal check counts the error note as progress: the run above stopped goal_met with nothing but ['[error] '] in notes. A run whose only output is a swallowed error should never read as success.

Repro:

mkdir /tmp/w && cd /tmp/w && touch a.txt
grapharc go "list the files here" --model claude-cli/sonnet --default --workspace /tmp/w --trace /tmp/w/t.jsonl
# state: findings=['[error] '], stop: goal_met

🤖 Generated with Claude Code

Activity

  1. Shashankss1205 commented on Aug 8, 2026

    @Shashankss1205
    CollaboratorAuthor

    Fixed on main in #99. All three parts of this report, each with a regression test.

    1. The delegated call failing where a direct one succeeds. The root cause was not in the adapter's hardened invocation — it was that the stdlib registry could not delegate at all. default_harness builds a Harness with an explicit LocalExecutor, and Harness.__init__ accepted workspace= only to construct a default SandboxedExecutor — so passing an executor silently discarded the workspace. AgentNode._run_delegated then asked the executor for a workspace, found none, and refused before spawning anything. Every agent phase of the one registry-plus-backend combination whose documented behaviour is delegation failed the same way.

    Harness now records its workspace whichever executor is in play, default_harness names it, and _run_delegated asks the harness before the executor. Tests: test_a_harness_keeps_the_workspace_it_was_given, test_the_stdlib_harness_names_a_workspace_so_claude_code_can_be_delegated_to.

    2. The empty error message. AgentResult.output is empty by contract for every termination_reason except TARGET_MET — the mid-work text lives in partial_output — and stdlib._agent_factory formatted output for the curtailed branch. So [error] was not a lost exception message; it was the right message being read out of the wrong field. It reads partial_output, then note, then "(no output)". Test: test_a_curtailed_phase_reports_what_it_managed_to_say.

    That is also why the underlying failure in (1) was invisible: the real string — "the delegated executor needs a workspace directory, and this harness's executor (LocalExecutor) does not expose one" — was being written and then dropped on the floor.

    3. The goal check counting an error as progress. goal_met was len(notes) >= 1. It now ignores notes that a phase wrote to say it did not finish, recognised by a marker built from the StopReason enum rather than a hardcoded string, so a new stop reason cannot silently start reading as success. A run whose only notes are curtailed no longer stops goal_met. Tests: test_a_run_whose_only_note_is_an_error_has_not_met_its_goal, test_a_real_report_still_meets_the_goal, test_curtailed_is_read_off_the_stop_reasons_not_guessed.

    Verified end to end afterwards: grapharc plan "…" --model claude-cli --registry grapharc.stdlib:build_registry --go now runs its delegated phases, and the recording of one is in docs/demo/.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions