From de7edd5bb129ec461bd0c4a5ad95dc1d9fe070ee Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 29 May 2026 14:00:32 +0900 Subject: [PATCH] fix: suppress config deprecation stderr in JSON mode globally (#824) Add SUPPRESS_CONFIG_WARNINGS_STDERR AtomicBool flag in runtime/config.rs and expose suppress_config_warnings_for_json_mode() via runtime crate. In main.rs, scan raw argv for --output-format json before parse_args and activate the flag so no settings-load warnings reach stderr on any JSON-mode surface (status, sandbox, system-prompt, mcp list, skills list, agents list, --resume /config*, etc.). Text-mode surfaces are unaffected; prose deprecation warnings continue to appear on stderr. All 572+ tests pass (one pre-existing worker_boot failure unrelated). --- rust/crates/runtime/src/config.rs | 15 +++++++++++++++ rust/crates/runtime/src/lib.rs | 12 ++++++------ rust/crates/rusty-claude-cli/src/main.rs | 10 ++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/rust/crates/runtime/src/config.rs b/rust/crates/runtime/src/config.rs index 8cbef7ab..0379e31b 100644 --- a/rust/crates/runtime/src/config.rs +++ b/rust/crates/runtime/src/config.rs @@ -10,7 +10,22 @@ use std::sync::Mutex; static EMITTED_CONFIG_WARNINGS: std::sync::OnceLock>> = std::sync::OnceLock::new(); +/// When set to `true`, `emit_config_warning_once` silently drops all prose +/// deprecation warnings instead of writing them to stderr. Set this flag +/// before any settings load when `--output-format json` is active so that +/// JSON-mode machine consumers see empty stderr on success. (#824) +static SUPPRESS_CONFIG_WARNINGS_STDERR: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(false); + +/// Call this once at startup when `--output-format json` is active. +pub fn suppress_config_warnings_for_json_mode() { + SUPPRESS_CONFIG_WARNINGS_STDERR.store(true, std::sync::atomic::Ordering::Relaxed); +} + fn emit_config_warning_once(warning: &str) { + if SUPPRESS_CONFIG_WARNINGS_STDERR.load(std::sync::atomic::Ordering::Relaxed) { + return; + } let set = EMITTED_CONFIG_WARNINGS.get_or_init(|| Mutex::new(HashSet::new())); let mut guard = set.lock().unwrap_or_else(|e| e.into_inner()); if guard.insert(warning.to_string()) { diff --git a/rust/crates/runtime/src/lib.rs b/rust/crates/runtime/src/lib.rs index 35d8bf02..f0ab67c3 100644 --- a/rust/crates/runtime/src/lib.rs +++ b/rust/crates/runtime/src/lib.rs @@ -65,12 +65,12 @@ pub use compact::{ get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult, }; pub use config::{ - ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpConfigCollection, - McpManagedProxyServerConfig, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig, - McpServerConfig, McpStdioServerConfig, McpTransport, McpWebSocketServerConfig, OAuthConfig, - ProviderFallbackConfig, ResolvedPermissionMode, RuntimeConfig, RuntimeFeatureConfig, - RuntimeHookConfig, RuntimePermissionRuleConfig, RuntimePluginConfig, ScopedMcpServerConfig, - CLAW_SETTINGS_SCHEMA_NAME, + suppress_config_warnings_for_json_mode, ConfigEntry, ConfigError, ConfigLoader, ConfigSource, + McpConfigCollection, McpManagedProxyServerConfig, McpOAuthConfig, McpRemoteServerConfig, + McpSdkServerConfig, McpServerConfig, McpStdioServerConfig, McpTransport, + McpWebSocketServerConfig, OAuthConfig, ProviderFallbackConfig, ResolvedPermissionMode, + RuntimeConfig, RuntimeFeatureConfig, RuntimeHookConfig, RuntimePermissionRuleConfig, + RuntimePluginConfig, ScopedMcpServerConfig, CLAW_SETTINGS_SCHEMA_NAME, }; pub use config_validate::{ check_unsupported_format, format_diagnostics, validate_config_file, ConfigDiagnostic, diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 785a4303..a3518264 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -532,6 +532,16 @@ fn plugin_load_failure_json(failure: &plugins::PluginLoadFailure) -> Value { fn run() -> Result<(), Box> { let args: Vec = env::args().skip(1).collect(); + // #824: suppress config deprecation prose warnings to stderr when JSON + // output mode is active. Scan the raw argv before parse_args so the + // suppression is in place before any settings file is loaded. + let json_mode = args + .windows(2) + .any(|w| w[0] == "--output-format" && w[1] == "json") + || args.iter().any(|a| a == "--output-format=json"); + if json_mode { + runtime::suppress_config_warnings_for_json_mode(); + } match parse_args(&args)? { CliAction::DumpManifests { output_format,