CLI Binary Not Found
Agent-Ctrl usesCliBinaryGuard to verify that the required CLI binary (claude, codex, opencode, pi, or gemini) is available before every execution. If the binary cannot be found, a RuntimeException is thrown immediately — before any prompt is sent to the agent.
The error message identifies the missing binary and provides installation guidance:
Resolution Steps
-
Verify installation. Run the CLI binary directly in your terminal to confirm it is installed:
- Complete authentication. Most agents require interactive authentication on first use. Run the CLI interactively at least once to complete any setup flows before using it through Agent-Ctrl.
-
Check PATH visibility. The binary must be in the system
PATHvisible to your PHP process. This is not always the same as your shell’sPATH. If you installed the CLI in a non-standard location (e.g., vianvmor a custom prefix), ensure that location is included in thePATHused by your web server, supervisor, or CLI runner. You can verify the PATH available to PHP: -
Consider the sandbox driver. The binary preflight check behaves differently depending on the sandbox driver:
- Host, Firejail, Bubblewrap — The binary must be available on the host system. The guard checks the host
PATH. - Docker, Podman — The guard skips the preflight check entirely, because the binary is expected to be inside the container image.
- Host, Firejail, Bubblewrap — The binary must be available on the host system. The guard checks the host
Working Directory Problems
When you callinDirectory(), the bridge validates that the directory exists before changing into it. If it does not exist, an InvalidArgumentException is thrown with the path:
Common Causes
- Typos or relative paths. Always use absolute paths. Relative paths resolve against the PHP process’s current working directory, which may differ from what you expect.
- Deleted or unmounted directories. The directory may have been removed or unmounted between the time you configured the builder and the time execution begins.
- Permission issues. The PHP process may not have permission to access the directory. Check file permissions with
ls -laand ensure the user running PHP has read (and possibly write) access.
Concurrency Warning
inDirectory() changes the PHP process’s current working directory using chdir() for the duration of the execution and restores it afterward. If your PHP process serves multiple requests concurrently (e.g., with Swoole, RoadRunner, or a threaded server), working directory changes affect the entire process. In concurrent environments, ensure that each request either uses absolute paths exclusively or coordinates directory changes carefully.
Non-Zero Exit Codes
Agent-Ctrl treats exit codes as data, not errors. A completed execution with a non-zero exit code does not throw an exception. It is your responsibility to check the result:Common Exit Codes
| Exit Code | Typical Cause |
|---|---|
| 0 | Successful completion |
| 1 | General error — invalid configuration, agent-side failure, or unhandled exception within the agent |
| 2 | Invalid arguments passed to the CLI binary |
| 124 | Process killed due to timeout (SIGTERM) |
| 137 | Process killed due to timeout (SIGKILL, after SIGTERM grace period) |
text() even when isSuccess() returns false.
Timeout Issues
The default timeout is 120 seconds. Complex tasks, large codebases, or agents that perform many tool calls may need significantly more time:- The sandbox executor sends a termination signal to the process.
- The response contains whatever output was produced before the timeout.
- The exit code will be non-zero (typically 124 or 137).
isSuccess()will returnfalse.
Guideline for Timeout Values
| Task Type | Suggested Timeout |
|---|---|
| Simple questions or summaries | 60-120 seconds |
| Code review of a single file | 120-300 seconds |
| Multi-file refactoring | 300-600 seconds |
| Full codebase analysis | 600-900 seconds |
| Complex multi-step tasks | 600+ seconds |
Streaming vs. Process Errors
There are two distinct categories of errors during execution, and they are handled differently.Stream Errors (Operational)
These are error events emitted by the agent during normal streaming. They represent issues the agent encountered while working — a tool failure, a rate limit, an API error, or a malformed request. They are delivered through theonError() callback:
AgentResponse is still returned normally.
Process Errors (Fatal)
These are PHP exceptions thrown when something goes fundamentally wrong — the binary is missing, the working directory does not exist, the process cannot be started, or the sandbox executor encounters a fatal error. These exceptions propagate normally and must be caught with try/catch:Parse Failures
Agent-Ctrl parses each agent’s JSON Lines output to extract text, tool calls, session IDs, and metadata. If a line contains malformed JSON, the behavior depends on the parser’s fail-fast setting:Fail-Fast Mode (Default)
When fail-fast is enabled, aJsonParsingException is thrown immediately upon encountering malformed JSON. This is the default behavior and ensures that corrupt data does not silently corrupt your results:
Tolerant Mode
When fail-fast is disabled (configured at the bridge level), malformed lines are silently skipped and counted. After execution, you can inspect the parse diagnostics:Common Causes of Parse Failures
- CLI version mismatch. The agent’s CLI tool was updated and its output format changed. Update Agent-Ctrl to the latest version.
- Debug output mixed into the stream. The agent’s CLI is emitting debug messages, warnings, or progress indicators alongside its JSON output. Check the CLI’s verbose/quiet settings.
- Corrupted process output. The process was interrupted mid-line, producing incomplete JSON. This can happen during timeouts or system resource exhaustion.
Debugging with the Console Logger
Agent-Ctrl includes a built-inAgentCtrlConsoleLogger that displays detailed, color-coded execution telemetry. This is invaluable for understanding the execution flow, diagnosing performance bottlenecks, and identifying where problems occur.
Basic Usage
Configuration Options
The logger accepts several constructor parameters to control what is displayed:Event Categories
The logger groups events into categories with color-coded labels:| Label | Color | Events |
|---|---|---|
EXEC | Cyan | Execution started |
DONE | Green | Execution completed (with exit code, tool count, cost, tokens) |
FAIL | Red | Error occurred |
TOOL | Yellow | Tool used (with name and arguments) |
TEXT | Gray | Text received (with length) |
PROC | Cyan | Process started / completed |
SBOX | Blue | Sandbox initialized / policy configured / ready |
STRM | Gray | Stream processing started / completed |
REQT | Gray | Request built |
CMD | Gray | Command spec created |
RESP | Gray | Response parsing started / data extracted / completed |
Example Output
Common Pitfalls
UsingcontinueSession() with the wrong agent. Session IDs are agent-specific. Session IDs are agent-specific and incompatible across bridges. Always use the same agent type when continuing or resuming a session.
Forgetting to check isSuccess(). A completed execution with a non-zero exit code does not throw an exception. Always verify the result before using the text output as authoritative.
Setting very short timeouts. Agents need time to start up, read context, and execute tools. Timeouts under 30 seconds may cause premature termination for anything beyond trivial prompts.
Relative paths in inDirectory(). Always use absolute paths. Relative paths resolve against the PHP process’s current working directory, which may differ from your expectations depending on how the process was started.
Running in concurrent PHP environments. The inDirectory() method uses chdir(), which affects the entire PHP process. In Swoole, RoadRunner, or other concurrent PHP environments, this can cause race conditions between requests. Use absolute paths throughout and avoid inDirectory() if possible, or ensure proper isolation.
Ignoring parse failures. If parseFailures() returns a non-zero value, some of the agent’s output was not processed. This may mean missing tool calls, incomplete text, or lost metadata. Investigate the parseFailureSamples() to determine the cause.