From afb5539d53c13ab4ee71f85058f6072e06cdc19d Mon Sep 17 00:00:00 2001 From: Philipp Henkel Date: Wed, 13 May 2026 17:43:14 +0200 Subject: [PATCH] fix: convert quoted numeric config values to numbers if needed (#8844) Signed-off-by: Philipp Henkel --- crates/goose/src/config/base.rs | 64 +++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/crates/goose/src/config/base.rs b/crates/goose/src/config/base.rs index cf239c1f71..acead899de 100644 --- a/crates/goose/src/config/base.rs +++ b/crates/goose/src/config/base.rs @@ -687,10 +687,20 @@ impl Config { } let values = self.load()?; - values + let value = values .get(key) - .ok_or_else(|| ConfigError::NotFound(key.to_string())) - .and_then(|v| Ok(serde_yaml::from_value(v.clone())?)) + .ok_or_else(|| ConfigError::NotFound(key.to_string()))?; + + match serde_yaml::from_value(value.clone()) { + Ok(value) => Ok(value), + Err(yaml_err) => { + let Some(string_value) = value.as_str() else { + return Err(yaml_err.into()); + }; + let parsed = Self::parse_env_value(string_value)?; + serde_json::from_value(parsed).map_err(|_| yaml_err.into()) + } + } } /// Set a configuration value in the config file (non-secret). @@ -1092,6 +1102,54 @@ mod tests { assert!(matches!(result, Err(ConfigError::NotFound(_)))); } + #[test] + fn test_get_param_reads_numeric_yaml_as_u64() -> Result<(), ConfigError> { + let _guard = env_lock::lock_env([("XXX_TIMEOUT", None::<&str>)]); + let config = new_test_config(); + + config.set_param("XXX_TIMEOUT", 300_u64)?; + + let value: u64 = config.get_param("XXX_TIMEOUT")?; + assert_eq!(value, 300); + Ok(()) + } + + #[test] + fn test_get_param_reads_quoted_numeric_yaml_as_u64() -> Result<(), ConfigError> { + let _guard = env_lock::lock_env([("XXX_TIMEOUT", None::<&str>)]); + let config = new_test_config(); + + config.set_param("XXX_TIMEOUT", "300")?; + + let value: u64 = config.get_param("XXX_TIMEOUT")?; + assert_eq!(value, 300); + Ok(()) + } + + #[test] + fn test_get_param_reads_quoted_numeric_yaml_as_string() -> Result<(), ConfigError> { + let _guard = env_lock::lock_env([("XXX_TIMEOUT", None::<&str>)]); + let config = new_test_config(); + + config.set_param("XXX_TIMEOUT", "300")?; + + let value: String = config.get_param("XXX_TIMEOUT")?; + assert_eq!(value, "300"); + Ok(()) + } + + #[test] + fn test_get_param_rejects_invalid_string_as_u64() -> Result<(), ConfigError> { + let _guard = env_lock::lock_env([("XXX_TIMEOUT", None::<&str>)]); + let config = new_test_config(); + + config.set_param("XXX_TIMEOUT", "invalid")?; + + let result: Result = config.get_param("XXX_TIMEOUT"); + assert!(matches!(result, Err(ConfigError::DeserializeError(_)))); + Ok(()) + } + #[test] fn test_yaml_formatting() -> Result<(), ConfigError> { let config_file = NamedTempFile::new().unwrap();