Checked other resources
Package (Required)
Related Issues / PRs
Reproduction Steps / Example Code (Python)
import tempfile
from pathlib import Path
from langchain.agents.middleware._execution import HostExecutionPolicy
from langchain.agents.middleware.shell_tool import ShellSession
policy = HostExecutionPolicy(command_timeout=0.5)
session = ShellSession(
workspace=Path(tempfile.mkdtemp(prefix="lc-shell-repro-")),
policy=policy,
command=("/bin/bash",),
environment={},
)
session.start()
try:
result = session.execute("printf 'hello without newline'", timeout=policy.command_timeout)
print(result)
finally:
session.stop(1.0)
Error Message and Stack Trace (if applicable)
There is no Python exception. The bug is behavioral.
Expected:
CommandExecutionResult(output='hello without newline', exit_code=0, timed_out=False, ...)
Actual on current `master` before the fix:
CommandExecutionResult(output='', exit_code=None, timed_out=True, ...)
Description
ShellSession.execute() currently injects a completion marker into stdout using a separate printf call, and _collect_output() only recognizes command completion when a stdout line starts with that marker.
However, stdout is read with readline(). If the command output does not end with a trailing newline, the command's final stdout bytes and the injected done marker are returned as a single line, for example:
hello without newline__LC_SHELL_DONE__<id> 0
In that case, the current logic:
if source == "stdout" and data.startswith(marker):
misses the marker because it no longer appears at the start of the line. The command is then treated as still running until command_timeout is reached.
This affects minimal cases such as printf 'hello without newline' and can also affect file reads where the file does not end with a newline.
A minimal fix is to detect the marker anywhere in the stdout line, preserve any real stdout content that appears before it, and still parse the exit code from the marker suffix.
System Info
System Information
------------------
> OS: Linux
> OS Version: #1 SMP Fri Mar 6 10:10:19 UTC 2026
> Python Version: 3.13.12 (main, Feb 12 2026, 00:43:56) [Clang 21.1.4 ]
Package Information
-------------------
> langchain_core: 1.3.0a1
> langchain: 1.2.15
> langsmith: 0.6.3
> langchain_openai: 1.1.12
> langchain_tests: 1.1.6
> langgraph_sdk: 0.3.3
Optional packages not installed
-------------------------------
> deepagents
> deepagents-cli
Other Dependencies
------------------
> httpx: 0.28.1
> jsonpatch: 1.33
> langgraph: 1.1.5
> numpy: 2.3.4
> openai: 2.26.0
> orjson: 3.11.6
> packaging: 24.2
> pydantic: 2.12.5
> pytest: 9.0.2
> pytest-asyncio: 1.3.0
> pytest-benchmark: 5.2.3
> pytest-codspeed: 4.2.0
> pytest-recording: 0.13.4
> pytest-socket: 0.7.0
> pyyaml: 6.0.3
> requests: 2.33.0
> requests-toolbelt: 1.0.0
> rich: 14.2.0
> syrupy: 5.1.0
> tenacity: 9.1.2
> tiktoken: 0.12.0
> typing-extensions: 4.15.0
> uuid-utils: 0.12.0
> vcrpy: 8.1.1
> zstandard: 0.25.0
Checked other resources
Package (Required)
Related Issues / PRs
ShellSession.execute()#34535Reproduction Steps / Example Code (Python)
Error Message and Stack Trace (if applicable)
Description
ShellSession.execute()currently injects a completion marker into stdout using a separateprintfcall, and_collect_output()only recognizes command completion when a stdout line starts with that marker.However, stdout is read with
readline(). If the command output does not end with a trailing newline, the command's final stdout bytes and the injected done marker are returned as a single line, for example:In that case, the current logic:
misses the marker because it no longer appears at the start of the line. The command is then treated as still running until
command_timeoutis reached.This affects minimal cases such as
printf 'hello without newline'and can also affect file reads where the file does not end with a newline.A minimal fix is to detect the marker anywhere in the stdout line, preserve any real stdout content that appears before it, and still parse the exit code from the marker suffix.
System Info