charmbracelet/crush · error
failed to update preferred small model: %w
Error message
failed to update preferred small model: %w
What it means
Same as the large-model case: when the preferred small model needed a fallback correction, Load() persists it to the global config and returns this wrapped error if the write fails.
Source
Thrown at internal/config/load.go:161
if err != nil {
return nil, fmt.Errorf("failed to configure selected models: %w", err)
}
cfg.Models[SelectedModelTypeLarge] = resolved.Large
cfg.Models[SelectedModelTypeSmall] = resolved.Small
// Persist any fallback corrections while we still hold writeMu.
if resolved.LargeFallback {
if err := store.updateLocked(ScopeGlobal, func(c *Config) map[string]any {
return store.updatePreferredModelFields(c, SelectedModelTypeLarge, resolved.Large)
}); err != nil {
return nil, fmt.Errorf("failed to update preferred large model: %w", err)
}
}
if resolved.SmallFallback {
if err := store.updateLocked(ScopeGlobal, func(c *Config) map[string]any {
return store.updatePreferredModelFields(c, SelectedModelTypeSmall, resolved.Small)
}); err != nil {
return nil, fmt.Errorf("failed to update preferred small model: %w", err)
}
}
store.SetupAgents()
// Capture initial staleness snapshot
// Capture initial staleness snapshot. Track every discovered config path,
// not just the ones that loaded, so a config file created after startup
// (e.g. a crushrc added mid-session) is detected as a change.
store.captureStalenessSnapshot(append(slices.Clone(configPaths), loadedPaths...))
return store, nil
}
// mustMarshalConfig marshals the config to JSON bytes, returning empty JSON on
// error.
func mustMarshalConfig(cfg *Config) []byte {
data, err := json.Marshal(cfg)
if err != nil {View on GitHub (pinned to 7944b8e522)
Solutions
- Make the global config file writable (chmod/chown)
- Validate and repair the global config JSON
- Check disk space
- Explicitly set a valid small preferred model in config so no fallback correction is needed
Example fix
// before
"options": {"preferred_model": {"small": "gpt-3.5-turbo-0301"}} // stale, triggers fallback write
// after
"options": {"preferred_model": {"small": "openai/gpt-4o-mini"}} // valid, no fallback write needed Defensive patterns
Strategy: try-catch
Validate before calling
fi, err := os.Stat(globalConfigPath)
if err == nil && fi.Mode().Perm()&0o200 == 0 {
return fmt.Errorf("global config %s is not writable", globalConfigPath)
} Try / catch
store, err := config.Load(ctx, opts)
if err != nil {
if strings.Contains(err.Error(), "failed to update preferred small model:") {
return fmt.Errorf("global config unwritable or corrupt: %w", err)
}
return err
} Prevention
- Set a valid small preferred model explicitly to avoid fallback writes
- Keep the global config writable and valid JSON
- Avoid running crush as different users sharing one config dir
- Check disk space and file ownership after environment changes
When it happens
Trigger: resolved.SmallFallback is true and store.updateLocked(ScopeGlobal, updatePreferredModelFields...) errors while persisting the corrected small model selection.
Common situations: Read-only or unwritable global config; corrupted global crush.json; disk-full; containerized environments mounting the config dir read-only.
Related errors
- failed to persist docker mcp configuration: %w
- failed to update preferred large model: %w
- docker MCP started but failed to persist configuration: %w
- failed to load projects: %w
- failed to check init flag file: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/664d31665f68762c.
Report an issue: GitHub.