chenhg5/cc-connect · error
save model: %w
Error message
save model: %w
What it means
Error from the model-switch path for agents that do NOT implement ProviderSwitcher: when `persistConfig` is set, the engine calls `e.modelSaveFunc(target)` to persist the chosen model to config, and wraps any failure with "save model: %w". The switch still happened in memory — this is purely a persistence failure.
Source
Thrown at core/engine.go:9956
// persists the change so reloads keep the selected default.
func (e *Engine) switchModel(target string) (string, error) {
return e.switchModelOnAgent(e.agent, target, true)
}
// switchModelOnAgent applies a runtime model selection to the provided agent.
// When persistConfig is true, config-backed model/provider changes are saved so
// reloads keep the new default. Workspace-scoped runtime switches pass false.
func (e *Engine) switchModelOnAgent(agent Agent, target string, persistConfig bool) (string, error) {
switcher, ok := agent.(ModelSwitcher)
if !ok {
return target, nil
}
providerSwitcher, ok := agent.(ProviderSwitcher)
if !ok {
if persistConfig && e.modelSaveFunc != nil {
if err := e.modelSaveFunc(target); err != nil {
return "", fmt.Errorf("save model: %w", err)
}
}
switcher.SetModel(target)
return target, nil
}
active := providerSwitcher.GetActiveProvider()
if active == nil {
if persistConfig && e.modelSaveFunc != nil {
if err := e.modelSaveFunc(target); err != nil {
return "", fmt.Errorf("save model: %w", err)
}
}
switcher.SetModel(target)
return target, nil
}
providers := providerSwitcher.ListProviders()
updated, found := SetProviderModel(providers, active.Name, target)View on GitHub (pinned to 4000b2338a)
Solutions
- Check write permission on the config file and its directory (`ls -l` the path modelSaveFunc writes to)
- Ensure the config volume is writable in containers (not mounted :ro)
- Run with persistConfig disabled if you do not want the model choice saved
- Inspect the wrapped inner error (%w) for the exact cause — it names the failing write
Example fix
// before: read-only config path config_path = "/etc/cc-connect/config.toml" // root-owned, app user cannot write // after: user-writable config config_path = "~/.config/cc-connect/config.toml"
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(cfgPath); if err != nil || info.IsDir() { return err }; f, err := os.OpenFile(cfgPath, os.O_WRONLY, 0); if err != nil { return fmt.Errorf("config not writable: %w", err) }; f.Close() Try / catch
Wrap the model-switch call and, on an error containing "save model:", fall back to an in-memory-only switch and warn the user the choice is not persisted:
if _, err := e.SwitchModel(...); err != nil { slog.Warn("model switched but not persisted", "err", err) } Prevention
- Ensure config.toml and its directory are writable by the cc-connect user
- Do not mount the config directory read-only in containers
- Check disk space on the config volume
- Validate config path settings (CC_CONNECT_CONFIG) point to a real file, not a directory
When it happens
Trigger: /model <name> (or the model-switch API) with persistConfig=true on a non-provider-switcher agent, and the registered modelSaveFunc returns an error: config file unwritable, TOML serialization failure, or config path misconfigured.
Common situations: config.toml is read-only (running as wrong user, read-only mounted volume); CC_CONNECT_CONFIG points at a directory without write permission; disk full.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- save provider model %q: %w
- parse existing Agy hooks %s: %w
- marshal Agy hooks overlay: %w
- tmux: 'session' option is required (name of the tmux session
- tmux: session %q does not exist and auto_create is disabled
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/60641b3d52bcf7d2.
Report an issue: GitHub.