Skip to content

fix(cli): prevent plugin loading logs from polluting 'openclaw agent --json' stdout#51086

Open
tomi-claycosmos wants to merge 1 commit intoopenclaw:mainfrom
tomi-claycosmos:fix/agent-json-plugin-logs
Open

fix(cli): prevent plugin loading logs from polluting 'openclaw agent --json' stdout#51086
tomi-claycosmos wants to merge 1 commit intoopenclaw:mainfrom
tomi-claycosmos:fix/agent-json-plugin-logs

Conversation

@tomi-claycosmos
Copy link
Copy Markdown

Summary

When running openclaw agent --json, plugin loading logs were being printed to stdout, corrupting the JSON output.

Root cause: The agentCliCommand function did not redirect runtime logs to stderr when --json was used. This is the same class of bug that was already fixed in completion-cli.ts (see the routeLogsToStderr() call there).

Fix: Call routeLogsToStderr() at the start of agentCliCommand when opts.json is true. This redirects all console.log / defaultRuntime.log calls to stderr, keeping stdout clean for the pure JSON response.

Changes

  • src/commands/agent-via-gateway.ts: Import routeLogsToStderr and call it when opts.json is true

Testing

openclaw agent --json --message "hello" --to +15555550123
# Before: stdout was polluted with plugin loading messages
# After: stdout contains only clean JSON

Fixes #51076

@openclaw-barnacle openclaw-barnacle bot added commands Command implementations size: XS labels Mar 20, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 20, 2026

Greptile Summary

This PR attempts to fix stdout pollution from plugin loading logs when running openclaw agent --json by calling routeLogsToStderr() at the start of agentCliCommand. The intent is correct — routeLogsToStderr() is already used successfully in completion-cli.ts for the same class of problem — but the implementation has a critical flaw that makes --json output completely broken.

  • Root cause of the new bug: By the time agentCliCommand executes, enableConsoleCapture() has already patched console.log in run-main.ts. Once routeLogsToStderr() sets forceConsoleToStderr = true, all subsequent console.log calls — including the one that outputs the final JSON payload in agentViaGatewayCommand (runtime.log(JSON.stringify(response, null, 2))) — are redirected to process.stderr. The result: stdout is empty and the JSON appears on stderr.
  • Why completion-cli.ts avoids this: It writes its output with process.stdout.write(script + "\n"), which bypasses the patched console.log entirely.
  • Recommended fix: Change the JSON output in agentViaGatewayCommand from runtime.log(JSON.stringify(...)) to process.stdout.write(JSON.stringify(...) + "\n") so it is not subject to the stderr redirect.

Confidence Score: 1/5

  • This PR introduces a regression: openclaw agent --json will produce empty stdout and JSON on stderr, making the flag non-functional.
  • The fix correctly identifies that routeLogsToStderr() should be called, but the final JSON output in agentViaGatewayCommand is still emitted via runtime.logconsole.log, which is already intercepted by enableConsoleCapture. With forceConsoleToStderr = true, the JSON goes to stderr. This is a clear regression — the --json flag will produce no output on stdout after the fix.
  • src/commands/agent-via-gateway.ts — specifically the runtime.log(JSON.stringify(response, null, 2)) call in agentViaGatewayCommand (around line 160) needs to be changed to process.stdout.write.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/commands/agent-via-gateway.ts
Line: 184-186

Comment:
**JSON response redirected to stderr by same call**

Calling `routeLogsToStderr()` here patches `console.log` (via `enableConsoleCapture`, which runs before this point in `run-main.ts`) to write to `process.stderr` whenever `forceConsoleToStderr` is `true`. The problem is that the final JSON payload in `agentViaGatewayCommand` is emitted the same way:

```ts
// agentViaGatewayCommand (line ~160)
if (opts.json) {
  runtime.log(JSON.stringify(response, null, 2));  // calls console.log → now goes to stderr!
  return response;
}
```

`runtime.log` delegates to `console.log`, which is already patched. Once `routeLogsToStderr()` is called, *every* subsequent `console.log` call — including the one that outputs the JSON result — is redirected to `process.stderr`. This means `--json` stdout would be empty and the JSON would land on stderr instead.

Compare with `completion-cli.ts`, which writes its stdout output with `process.stdout.write(script + "\n")` directly, bypassing the console patch entirely.

The fix should write the JSON using `process.stdout.write` (or `rawConsole.log` / `loggingState.rawConsole`) so it is not subject to the `forceConsoleToStderr` redirect. For example, in `agentViaGatewayCommand`:

```ts
if (opts.json) {
  process.stdout.write(JSON.stringify(response, null, 2) + "\n");
  return response;
}
```

Without this change, `openclaw agent --json` will produce an empty stdout and the JSON on stderr — the opposite of the intended behaviour.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix(cli): route logs..."

Comment on lines +184 to +186
if (opts.json) {
routeLogsToStderr();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 JSON response redirected to stderr by same call

Calling routeLogsToStderr() here patches console.log (via enableConsoleCapture, which runs before this point in run-main.ts) to write to process.stderr whenever forceConsoleToStderr is true. The problem is that the final JSON payload in agentViaGatewayCommand is emitted the same way:

// agentViaGatewayCommand (line ~160)
if (opts.json) {
  runtime.log(JSON.stringify(response, null, 2));  // calls console.log → now goes to stderr!
  return response;
}

runtime.log delegates to console.log, which is already patched. Once routeLogsToStderr() is called, every subsequent console.log call — including the one that outputs the JSON result — is redirected to process.stderr. This means --json stdout would be empty and the JSON would land on stderr instead.

Compare with completion-cli.ts, which writes its stdout output with process.stdout.write(script + "\n") directly, bypassing the console patch entirely.

The fix should write the JSON using process.stdout.write (or rawConsole.log / loggingState.rawConsole) so it is not subject to the forceConsoleToStderr redirect. For example, in agentViaGatewayCommand:

if (opts.json) {
  process.stdout.write(JSON.stringify(response, null, 2) + "\n");
  return response;
}

Without this change, openclaw agent --json will produce an empty stdout and the JSON on stderr — the opposite of the intended behaviour.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/agent-via-gateway.ts
Line: 184-186

Comment:
**JSON response redirected to stderr by same call**

Calling `routeLogsToStderr()` here patches `console.log` (via `enableConsoleCapture`, which runs before this point in `run-main.ts`) to write to `process.stderr` whenever `forceConsoleToStderr` is `true`. The problem is that the final JSON payload in `agentViaGatewayCommand` is emitted the same way:

```ts
// agentViaGatewayCommand (line ~160)
if (opts.json) {
  runtime.log(JSON.stringify(response, null, 2));  // calls console.log → now goes to stderr!
  return response;
}
```

`runtime.log` delegates to `console.log`, which is already patched. Once `routeLogsToStderr()` is called, *every* subsequent `console.log` call — including the one that outputs the JSON result — is redirected to `process.stderr`. This means `--json` stdout would be empty and the JSON would land on stderr instead.

Compare with `completion-cli.ts`, which writes its stdout output with `process.stdout.write(script + "\n")` directly, bypassing the console patch entirely.

The fix should write the JSON using `process.stdout.write` (or `rawConsole.log` / `loggingState.rawConsole`) so it is not subject to the `forceConsoleToStderr` redirect. For example, in `agentViaGatewayCommand`:

```ts
if (opts.json) {
  process.stdout.write(JSON.stringify(response, null, 2) + "\n");
  return response;
}
```

Without this change, `openclaw agent --json` will produce an empty stdout and the JSON on stderr — the opposite of the intended behaviour.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7caf1239ad

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +184 to +185
if (opts.json) {
routeLogsToStderr();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep openclaw agent --json output on stdout

In the normal CLI entrypoint, src/cli/run-main.ts:113-115 enables console capture, and defaultRuntime.log is implemented with console.log (src/runtime.ts:21-29). routeLogsToStderr() flips all captured console.log output to stderr (src/logging/console.ts:115-117 and src/logging/console.ts:291-296), so this new call also redirects the later runtime.log(JSON.stringify(...)) in agentViaGatewayCommand (src/commands/agent-via-gateway.ts:159-161). That means openclaw agent --json now produces its JSON response on stderr instead of stdout, breaking the machine-readable contract for real CLI users even though the completion command pattern looked similar.

Useful? React with 👍 / 👎.

@tomi-claycosmos
Copy link
Copy Markdown
Author

Noted. The concern about JSON output going to stderr via routeLogsToStderr() is valid — I will switch to process.stdout.write for structured JSON to preserve the --json contract.

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

Labels

commands Command implementations size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openclaw agent --json output polluted by plugin loading logs

2 participants