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

  1. Check write permission on the config file and its directory (`ls -l` the path modelSaveFunc writes to)
  2. Ensure the config volume is writable in containers (not mounted :ro)
  3. Run with persistConfig disabled if you do not want the model choice saved
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/60641b3d52bcf7d2. Report an issue: GitHub.