{"record":{"id":"9da9544a9e9adc4f","repo":"charmbracelet/crush","slug":"failed-to-delete-config-field-s-w","errorCode":null,"errorMessage":"failed to delete config field %s: %w","messagePattern":"failed to delete config field (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/store.go","lineNumber":495,"sourceCode":"//\n// Caller must hold writeMu.\nfunc (s *ConfigStore) pinPreferredModelLocked(modelType SelectedModelType, model SelectedModel) {\n\tif s.overrides.Models == nil {\n\t\ts.overrides.Models = make(map[SelectedModelType]SelectedModel)\n\t}\n\ts.overrides.Models[modelType] = model\n}\n\n// RemoveConfigField removes a key from the config file for the given scope.\n// After a successful write, it automatically reloads config to keep in-memory\n// state fresh.\n//\n// The write is protected by an in-process mutex and a cross-process flock.\nfunc (s *ConfigStore) RemoveConfigField(scope Scope, key string) error {\n\terr := s.atomicWrite(scope, func(data []byte) ([]byte, error) {\n\t\tv, sErr := sjson.Delete(string(data), key)\n\t\tif sErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to delete config field %s: %w\", key, sErr)\n\t\t}\n\t\treturn []byte(v), nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := s.autoReload(context.Background()); err != nil {\n\t\tslog.Warn(\"Config file updated but failed to reload in-memory state\", \"error\", err)\n\t}\n\n\treturn nil\n}\n\n// UpdatePreferredModel updates the preferred model for the given type and\n// persists it to the config file at the given scope. The selected model and\n// the recent-models list are written together in a single config write.\n//","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/store.go#L477-L513","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\nstore.RemoveConfigField(scope, \"providers.\"+providerID+\".api_key\") // providerID contains a dot\n// after\nif strings.Contains(providerID, \".\") {\n    return fmt.Errorf(\"provider ID %q cannot be addressed as a config path\", providerID)\n}\nstore.RemoveConfigField(scope, \"providers.\"+providerID+\".api_key\")","handlingStrategy":"validation","validationCode":"raw, _ := os.ReadFile(configPath)\nif !json.Valid(raw) {\n    return fmt.Errorf(\"config JSON invalid; cannot delete field %s\", key)\n}\nif !gjson.GetBytes(raw, key).Exists() {\n    return nil // nothing to delete; skip the write entirely\n}","typeGuard":"func fieldExists(data []byte, path string) bool {\n    return gjson.GetBytes(data, path).Exists()\n}","tryCatchPattern":"if err := store.RemoveConfigField(scope, key); err != nil {\n    if strings.Contains(err.Error(), \"failed to delete config field\") {\n        return fmt.Errorf(\"cannot delete %s (check path parents are objects): %w\", key, err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["json","sjson","config-delete"],"backgroundTag":"json-path-delete-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}