fix(config): add startup validation to catch invalid config values early
Add Config::validate() called from load_or_init() after env overrides are applied. This catches obviously invalid configuration values at startup instead of allowing them to silently cause runtime failures. Validated fields: - gateway.host: must not be empty - autonomy.max_actions_per_hour: must be > 0 - scheduler.max_concurrent: must be > 0 - scheduler.max_tasks: must be > 0 - model_routes[*]: hint, provider, model must not be empty - embedding_routes[*]: hint, provider, model must not be empty - proxy: delegates to existing ProxyConfig::validate() Previously, ProxyConfig::validate() was only called during apply_env_overrides() and only warned/disabled on failure. The new Config::validate() runs it as a hard error after all overrides are resolved, ensuring proxy misconfiguration is surfaced early. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
77609777ab
commit
99cf2fdfee
1 changed files with 57 additions and 0 deletions
|
|
@ -2910,6 +2910,7 @@ impl Config {
|
||||||
decrypt_optional_secret(&store, &mut agent.api_key, "config.agents.*.api_key")?;
|
decrypt_optional_secret(&store, &mut agent.api_key, "config.agents.*.api_key")?;
|
||||||
}
|
}
|
||||||
config.apply_env_overrides();
|
config.apply_env_overrides();
|
||||||
|
config.validate()?;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
path = %config.config_path.display(),
|
path = %config.config_path.display(),
|
||||||
workspace = %config.workspace_dir.display(),
|
workspace = %config.workspace_dir.display(),
|
||||||
|
|
@ -2932,6 +2933,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.apply_env_overrides();
|
config.apply_env_overrides();
|
||||||
|
config.validate()?;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
path = %config.config_path.display(),
|
path = %config.config_path.display(),
|
||||||
workspace = %config.workspace_dir.display(),
|
workspace = %config.workspace_dir.display(),
|
||||||
|
|
@ -2943,6 +2945,61 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate configuration values that would cause runtime failures.
|
||||||
|
///
|
||||||
|
/// Called after TOML deserialization and env-override application to catch
|
||||||
|
/// obviously invalid values early instead of failing at arbitrary runtime points.
|
||||||
|
pub fn validate(&self) -> Result<()> {
|
||||||
|
// Gateway
|
||||||
|
if self.gateway.host.trim().is_empty() {
|
||||||
|
anyhow::bail!("gateway.host must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autonomy
|
||||||
|
if self.autonomy.max_actions_per_hour == 0 {
|
||||||
|
anyhow::bail!("autonomy.max_actions_per_hour must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scheduler
|
||||||
|
if self.scheduler.max_concurrent == 0 {
|
||||||
|
anyhow::bail!("scheduler.max_concurrent must be greater than 0");
|
||||||
|
}
|
||||||
|
if self.scheduler.max_tasks == 0 {
|
||||||
|
anyhow::bail!("scheduler.max_tasks must be greater than 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model routes
|
||||||
|
for (i, route) in self.model_routes.iter().enumerate() {
|
||||||
|
if route.hint.trim().is_empty() {
|
||||||
|
anyhow::bail!("model_routes[{i}].hint must not be empty");
|
||||||
|
}
|
||||||
|
if route.provider.trim().is_empty() {
|
||||||
|
anyhow::bail!("model_routes[{i}].provider must not be empty");
|
||||||
|
}
|
||||||
|
if route.model.trim().is_empty() {
|
||||||
|
anyhow::bail!("model_routes[{i}].model must not be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedding routes
|
||||||
|
for (i, route) in self.embedding_routes.iter().enumerate() {
|
||||||
|
if route.hint.trim().is_empty() {
|
||||||
|
anyhow::bail!("embedding_routes[{i}].hint must not be empty");
|
||||||
|
}
|
||||||
|
if route.provider.trim().is_empty() {
|
||||||
|
anyhow::bail!("embedding_routes[{i}].provider must not be empty");
|
||||||
|
}
|
||||||
|
if route.model.trim().is_empty() {
|
||||||
|
anyhow::bail!("embedding_routes[{i}].model must not be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy (delegate to existing validation)
|
||||||
|
self.proxy.validate()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply environment variable overrides to config
|
/// Apply environment variable overrides to config
|
||||||
pub fn apply_env_overrides(&mut self) {
|
pub fn apply_env_overrides(&mut self) {
|
||||||
// API Key: ZEROCLAW_API_KEY or API_KEY (generic)
|
// API Key: ZEROCLAW_API_KEY or API_KEY (generic)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue