openclaw - ✅(Solved) Fix [Bug]: Suppress non-JSON output when CLI option `--json` is specified [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#84293Fetched 2026-05-20 03:41:44
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
1
Author
Timeline (top)
labeled ×7commented ×1cross-referenced ×1

When running a CLI command like openclaw nodes list --json, the CLI initializes various componenents, like plugins, on startup, which may try to emit log lines on standard output without knowing that output should only be JSON.

Error Message

When CLI option --json is used, the output should always be pure JSON. Initialization of components like plugins should force their output to standard error regardless if they already do that themselves.

Root Cause

When running a CLI command like openclaw nodes list --json, the CLI initializes various componenents, like plugins, on startup, which may try to emit log lines on standard output without knowing that output should only be JSON.

Fix Action

Fixed

PR fix notes

PR #84323: fix(cli): keep nodes json startup output parseable

Description (problem / solution / changelog)

Summary

  • share the CLI JSON-output stderr routing helper used during plugin command registration
  • apply the same guard to the nodes sub-CLI plugin registration path
  • add regression coverage that nodes ... --json routes plugin registration diagnostics away from stdout while non-JSON invocations stay unchanged

Fixes #84293

Verification

  • node scripts/run-vitest.mjs src/cli/nodes-cli.register-json-output.test.ts
  • node scripts/run-vitest.mjs --config test/vitest/vitest.e2e.config.ts src/cli/program.nodes-basic.e2e.test.ts
  • node scripts/run-vitest.mjs --config test/vitest/vitest.e2e.config.ts test/cli-json-stdout.e2e.test.ts
  • corepack pnpm exec oxfmt --check src/cli/json-output-mode.ts src/cli/nodes-cli/register.ts src/cli/nodes-cli.register-json-output.test.ts src/cli/run-main.ts
  • git diff --check

Real behavior proof

  • Behavior addressed: nodes list --json startup diagnostics/config warnings are routed to stderr instead of stdout, keeping JSON stdout free of non-JSON log lines.
  • Real environment tested: Local OpenClaw source checkout on Linux, Node v22.19.0, branch fix/84293-json-nodes-stderr, using the real CLI entrypoint node scripts/run-node.mjs against the local configured gateway.
  • Exact steps or command run after this patch: node scripts/run-node.mjs nodes list --json >stdout.txt 2>stderr.txt; then checked exit status and byte counts for both streams.
  • Evidence after fix:
    status=1
    stdout bytes=0
    stderr bytes=488
    Config warnings:
    - plugins.entries.discord: plugin discord: duplicate plugin id detected; global plugin will be overridden by bundled plugin (.../dist/extensions/discord/index.js)
    gateway connect failed: GatewayClientRequestError: protocol mismatch
    nodes list failed: GatewayTransportError: gateway closed (1002): protocol mismatch
    Gateway target: ws://127.0.0.1:18789
    Source: local loopback
    Config: /home/june/.openclaw/openclaw.json
    Bind: loopback
  • Observed result after fix: No plugin/config diagnostic text was written to stdout; all observed warning/failure diagnostics were on stderr. The local command failed only because the already-running gateway had a protocol mismatch.
  • What was not tested: I did not run the reporter's Helm/Kubernetes environment or a successful paired-node gateway response; local proof covers stream routing on the real CLI path plus targeted regression tests cover the nodes ... --json plugin registration guard.

Changed files

  • src/cli/json-output-mode.ts (added, +30/-0)
  • src/cli/nodes-cli.register-json-output.test.ts (added, +51/-0)
  • src/cli/nodes-cli/register.ts (modified, +8/-4)
  • src/cli/run-main.ts (modified, +7/-39)

Code Example

node@openclaw-9ffcb8c66-bszj9:~/.openclaw$ openclaw nodes list --json 2> /dev/null

12:42:15 [plugins] [lcm] Transcript GC disabled (default false)
12:42:15 [plugins] [lcm] Proactive threshold compaction mode: deferred (default deferred)
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryDepthColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryMetadataColumns durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryModelColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessageIdentityHashColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessageLargeContentColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessagePartsTable durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=backfillMessageIdentityHashes durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=createMessagesIdentityHashIndex durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureCompactionTelemetryColumns durationMs=1
12:42:15 [plugins] [lcm] migration step complete: step=ensureFocusBriefTables durationMs=0
12:42:15 [plugins] [lcm] migration step skipped: step=backfillSummaryDepths algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step complete: step=createSummariesDepthIndex durationMs=0
12:42:15 [plugins] [lcm] migration step skipped: step=backfillSummaryMetadata algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step skipped: step=backfillToolCallColumns algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessagesFts durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummariesFts durationMs=1
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummariesFtsCjk durationMs=1
12:42:15 [plugins] [lcm] Migration run completed during engine init: duration=21ms fts5=undefined
12:42:15 [plugins] [lcm] Migration successful — 34 tables: conversations, sqlite_sequence, messages, summaries, message_parts, summary_messages, summary_parents, context_items, large_files, conversation_bootstrap_state, conversation_compaction_telemetry, conversation_compaction_maintenance, focus_briefs, focus_brief_sources, lcm_migration_state, messages_fts, messages_fts_data, messages_fts_idx, messages_fts_content, messages_fts_docsize, messages_fts_config, summaries_fts, summaries_fts_data, summaries_fts_idx, summaries_fts_content, summaries_fts_docsize, summaries_fts_config, summaries_fts_cjk, summaries_fts_cjk_data, summaries_fts_cjk_idx, summaries_fts_cjk_content, summaries_fts_cjk_docsize, summaries_fts_cjk_config, sqlite_stat1
12:42:15 [plugins] [lcm] Ignoring sessions matching 1 pattern(s) from plugin config: agent:*:cron:**
12:42:15 [plugins] [lcm] Engine initialized for db=/home/node/.openclaw/.openclaw/lcm.db duration=77ms
12:42:15 [plugins] [lcm] Plugin loaded (enabled=true, db=/home/node/.openclaw/.openclaw/lcm.db, threshold=0.75, proactiveThresholdCompactionMode=deferred)
12:42:15 [plugins] [lcm] State dir: /home/node/.openclaw/.openclaw
12:42:15 [plugins] [lcm] Compaction summarization model: openai-codex/gpt-5.4 (default)
12:42:15 [plugins] [lcm] auto-rotate: phase=startup action=summary thresholdBytes=2097152 durationMs=150 bytesRemoved=0 scanned=0 eligible=0 rotated=0 warned=0 skipped=0 backupCreated=0 reason=completed
{
  "pending": [],
  "paired": [
    {
      "nodeId": "2099a8b7974cbbc84ee424c2293817f602fd31cbd2afb96af366065c8cd90234",
      "platform": "darwin",
...
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

When running a CLI command like openclaw nodes list --json, the CLI initializes various componenents, like plugins, on startup, which may try to emit log lines on standard output without knowing that output should only be JSON.

Steps to reproduce

  1. In the agent container, run openclaw nodes list --json 2> /dev/null

Expected behavior

When CLI option --json is used, the output should always be pure JSON. Initialization of components like plugins should force their output to standard error regardless if they already do that themselves.

Actual behavior

CLI output contains non-JSON log lines, which causes JSON parsing errors.

OpenClaw version

2026.5.18

Operating system

Ubuntu 24.04

Install method

Kubernetes/Helm

Model

openai-codex/gpt-5.4

Provider / routing chain

openclaw -> litellm -> chatgpt

Additional provider/model setup details

No response

Logs, screenshots, and evidence

node@openclaw-9ffcb8c66-bszj9:~/.openclaw$ openclaw nodes list --json 2> /dev/null

12:42:15 [plugins] [lcm] Transcript GC disabled (default false)
12:42:15 [plugins] [lcm] Proactive threshold compaction mode: deferred (default deferred)
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryDepthColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryMetadataColumns durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummaryModelColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessageIdentityHashColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessageLargeContentColumn durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessagePartsTable durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=backfillMessageIdentityHashes durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=createMessagesIdentityHashIndex durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureCompactionTelemetryColumns durationMs=1
12:42:15 [plugins] [lcm] migration step complete: step=ensureFocusBriefTables durationMs=0
12:42:15 [plugins] [lcm] migration step skipped: step=backfillSummaryDepths algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step complete: step=createSummariesDepthIndex durationMs=0
12:42:15 [plugins] [lcm] migration step skipped: step=backfillSummaryMetadata algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step skipped: step=backfillToolCallColumns algorithmVersion=1 reason=already-complete
12:42:15 [plugins] [lcm] migration step complete: step=ensureMessagesFts durationMs=0
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummariesFts durationMs=1
12:42:15 [plugins] [lcm] migration step complete: step=ensureSummariesFtsCjk durationMs=1
12:42:15 [plugins] [lcm] Migration run completed during engine init: duration=21ms fts5=undefined
12:42:15 [plugins] [lcm] Migration successful — 34 tables: conversations, sqlite_sequence, messages, summaries, message_parts, summary_messages, summary_parents, context_items, large_files, conversation_bootstrap_state, conversation_compaction_telemetry, conversation_compaction_maintenance, focus_briefs, focus_brief_sources, lcm_migration_state, messages_fts, messages_fts_data, messages_fts_idx, messages_fts_content, messages_fts_docsize, messages_fts_config, summaries_fts, summaries_fts_data, summaries_fts_idx, summaries_fts_content, summaries_fts_docsize, summaries_fts_config, summaries_fts_cjk, summaries_fts_cjk_data, summaries_fts_cjk_idx, summaries_fts_cjk_content, summaries_fts_cjk_docsize, summaries_fts_cjk_config, sqlite_stat1
12:42:15 [plugins] [lcm] Ignoring sessions matching 1 pattern(s) from plugin config: agent:*:cron:**
12:42:15 [plugins] [lcm] Engine initialized for db=/home/node/.openclaw/.openclaw/lcm.db duration=77ms
12:42:15 [plugins] [lcm] Plugin loaded (enabled=true, db=/home/node/.openclaw/.openclaw/lcm.db, threshold=0.75, proactiveThresholdCompactionMode=deferred)
12:42:15 [plugins] [lcm] State dir: /home/node/.openclaw/.openclaw
12:42:15 [plugins] [lcm] Compaction summarization model: openai-codex/gpt-5.4 (default)
12:42:15 [plugins] [lcm] auto-rotate: phase=startup action=summary thresholdBytes=2097152 durationMs=150 bytesRemoved=0 scanned=0 eligible=0 rotated=0 warned=0 skipped=0 backupCreated=0 reason=completed
{
  "pending": [],
  "paired": [
    {
      "nodeId": "2099a8b7974cbbc84ee424c2293817f602fd31cbd2afb96af366065c8cd90234",
      "platform": "darwin",
...

Impact and severity

Automation scripts that expect pure JSON output fail to parse JSON output due to leading non-JSON log lines.

Additional information

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

When CLI option --json is used, the output should always be pure JSON. Initialization of components like plugins should force their output to standard error regardless if they already do that themselves.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: Suppress non-JSON output when CLI option `--json` is specified [1 pull requests, 1 comments, 2 participants]