charmbracelet/crush · error
failed to delete config field %s: %w
Error message
failed to delete config field %s: %w
What it means
RemoveConfigField deletes a key via sjson.Delete inside atomicWrite. This error wraps an sjson.Delete failure — the current file content could not be parsed as JSON or the path could not be resolved for deletion.
Source
Thrown at internal/config/store.go:495
//
// Caller must hold writeMu.
func (s *ConfigStore) pinPreferredModelLocked(modelType SelectedModelType, model SelectedModel) {
if s.overrides.Models == nil {
s.overrides.Models = make(map[SelectedModelType]SelectedModel)
}
s.overrides.Models[modelType] = model
}
// RemoveConfigField removes a key from the config file for the given scope.
// After a successful write, it automatically reloads config to keep in-memory
// state fresh.
//
// The write is protected by an in-process mutex and a cross-process flock.
func (s *ConfigStore) RemoveConfigField(scope Scope, key string) error {
err := s.atomicWrite(scope, func(data []byte) ([]byte, error) {
v, sErr := sjson.Delete(string(data), key)
if sErr != nil {
return nil, fmt.Errorf("failed to delete config field %s: %w", key, sErr)
}
return []byte(v), nil
})
if err != nil {
return err
}
if err := s.autoReload(context.Background()); err != nil {
slog.Warn("Config file updated but failed to reload in-memory state", "error", err)
}
return nil
}
// UpdatePreferredModel updates the preferred model for the given type and
// persists it to the config file at the given scope. The selected model and
// the recent-models list are written together in a single config write.
//View on GitHub (pinned to 7944b8e522)
Solutions
- Validate the config JSON (jq . <configfile>) and repair syntax errors
- Confirm the target path exists and its parents are objects/arrays
- Use the exact key path that was used to set the field (same escaping)
- Back up and regenerate the config if it is corrupted
Example fix
// before
store.RemoveConfigField(scope, "providers."+providerID+".api_key") // providerID contains a dot
// after
if strings.Contains(providerID, ".") {
return fmt.Errorf("provider ID %q cannot be addressed as a config path", providerID)
}
store.RemoveConfigField(scope, "providers."+providerID+".api_key") Defensive patterns
Strategy: validation
Validate before calling
raw, _ := os.ReadFile(configPath)
if !json.Valid(raw) {
return fmt.Errorf("config JSON invalid; cannot delete field %s", key)
}
if !gjson.GetBytes(raw, key).Exists() {
return nil // nothing to delete; skip the write entirely
} Type guard
func fieldExists(data []byte, path string) bool {
return gjson.GetBytes(data, path).Exists()
} Try / catch
if err := store.RemoveConfigField(scope, key); err != nil {
if strings.Contains(err.Error(), "failed to delete config field") {
return fmt.Errorf("cannot delete %s (check path parents are objects): %w", key, err)
}
return err
} Prevention
- Check field existence with gjson before deleting to avoid pointless writes
- Keep config JSON valid — validate after every manual edit
- Use identical path escaping for set/delete pairs
- Back up the config before bulk deletions
When it happens
Trigger: Calling RemoveConfigField when the config file contains invalid JSON, or the key path traverses a non-object/non-array node (e.g. deleting providers.foo.bar when providers.foo is a string), or malformed path syntax.
Common situations: Corrupted or hand-edited config; deleting nested keys under a key that was overwritten with a scalar; keys containing dots interpreted as nested paths; race where another process wrote invalid JSON between read and delete (mitigated by flock but possible with external writers).
Related errors
- failed to set config field %s: %w
- failed to decode response: %w
- error parsing parameters: %s
- questions must be an array: %w
- failed to marshal GraphQL request: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/9da9544a9e9adc4f.
Report an issue: GitHub.