Agent defaults: "Couldn't save." discards the backend validation message, making the failure undiagnosable
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
`AgentDefaultsEditor` discards the backend's validation message and shows a bare **"Couldn't save."** The backend's reason is accurate and actionable — it just never reaches the user.
Result: a save that can never succeed, with no indication of why, anywhere. No app log, no relay log (even at DEBUG), no config file touched.
## Reproduce
1. Put a derived provider/model key into the global agent config's `env_vars` — e.g. `GOOSE_PROVIDER` or `GOOSE_MODEL` (easy to do if you have edited `global-agent-config.json` directly, e.g. as a workaround for a provider issue).
2. Open **Agents → Agent defaults**, change the harness or model, press **Save defaults**.
3. Every save fails with `Couldn't save.` and no further detail.
## Cause
The backend correctly rejects it — `desktop/src-tauri/src/managed_agents/global_config/mod.rs:113`:
```rust
if !derived.is_empty() {
return Err(format!(
"the following keys must be set via the structured provider/model fields, \
not as env vars: {}",
derived.join(", ")
));
}
```
against `DERIVED_PROVIDER_MODEL_ENV_KEYS` (`GOOSE_MODEL`, `GOOSE_PROVIDER`, `BUZZ_AGENT_MODEL`, `BUZZ_AGENT_PROVIDER`).
The rule itself is right — Buzz derives those vars from the structured fields, so allowing both would create two sources of truth. The problem is purely that the message is thrown away in `desktop/src/features/agents/ui/AgentDefaultsEditor.tsx:233`:
```js
} catch (err) {
setSaveState("error");
setSaveError(typeof err === "string" ? err : "Couldn't save.");
}
```
`set_global_agent_config` returns `Result`, so the reason *is* a string on the Rust side — but whatever reaches the `catch` fails the `typeof err === "string"` test (Tauri appears to wrap it), so the fallback always wins.
## Suggested fix
Unwrap the Tauri error rather than testing it as a bare string, e.g.:
```js
const message =
typeof err === "string" ? err :
(err as { message?: string })?.message ?? "Couldn't save.";
setSaveError(message);
```
Even surfacing `String(err)` would be a large improvement over the current dead end.
## Impact
The validation is a guard rail users are quite likely to hit — anyone who has hand-edited `global-agent-config.json` to work around a provider problem will land on it. As shipped, the only way to discover the cause is to read the Rust source.
## Environment
- Buzz Desktop 0.5.x, macOS arm64
- Relay: self-hosted, built from source
## Related
Same family as #3636 and #4278 — a correct signal produced internally, then discarded before it reaches the operator.
Contributor guide
Assessment
This issue has not been assessed yet.