{"record":{"id":"a42691763e3bd367","repo":"charmbracelet/crush","slug":"invalid-json-in-config-file-s-a42691","errorCode":null,"errorMessage":"invalid JSON in config file %s","messagePattern":"invalid JSON in config file (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/store.go","lineNumber":1197,"sourceCode":"\n\tconfigPaths := lookupConfigs(s.workingDir)\n\tcfg, loadedPaths, err := loadFromConfigPaths(ctx, configPaths)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to reload config: %w\", err)\n\t}\n\n\t// Apply defaults (using existing data directory if set)\n\tvar dataDir string\n\tif cur := s.Config(); cur != nil && cur.Options != nil {\n\t\tdataDir = cur.Options.DataDirectory\n\t}\n\tcfg.setDefaults(s.workingDir, dataDir)\n\n\t// Merge workspace config if present\n\tworkspacePath := filepath.Join(cfg.Options.DataDirectory, fmt.Sprintf(\"%s.json\", appName))\n\tif wsData, err := os.ReadFile(workspacePath); err == nil && len(wsData) > 0 {\n\t\tif !json.Valid(wsData) {\n\t\t\treturn fmt.Errorf(\"invalid JSON in config file %s\", workspacePath)\n\t\t}\n\t\tmerged, mergeErr := loadFromBytes(append([][]byte{mustMarshalConfig(cfg)}, wsData))\n\t\tif mergeErr == nil {\n\t\t\tdataDir := cfg.Options.DataDirectory\n\t\t\t*cfg = *merged\n\t\t\tcfg.setDefaults(s.workingDir, dataDir)\n\t\t\tloadedPaths = append(loadedPaths, workspacePath)\n\t\t}\n\t}\n\n\t// Validate hooks after all config merging is complete so matcher\n\t// regexes are recompiled on the reloaded config (mirrors Load).\n\tif err := cfg.ValidateHooks(); err != nil {\n\t\treturn fmt.Errorf(\"invalid hook configuration on reload: %w\", err)\n\t}\n\n\t// Save current state for potential rollback BEFORE configureProviders,\n\t// which may write to disk via RemoveConfigField (e.g. removing stale","sourceCodeStart":1179,"sourceCodeEnd":1215,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/store.go#L1179-L1215","documentation":"After the base reload, the store merges an optional workspace-level config file (<dataDirectory>/<appName>.json). If that file exists and its bytes are not valid JSON (json.Valid fails), reload aborts with this error rather than silently ignoring the corrupt workspace overrides.","triggerScenarios":"os.ReadFile succeeds on <DataDirectory>/<appName>.json with non-empty content and json.Valid(wsData) returns false — e.g. trailing commas, comments, truncated write, or a non-JSON file placed at that path.","commonSituations":"A crashed previous run left a partially written workspace config; a user pasted YAML or JSONC into the .json file; editor auto-save wrote a truncated file.","solutions":["Run the file through a JSON linter (jq . <dataDir>/crush.json) and fix the syntax error.","Remove or rename the corrupt workspace file if the overrides are not needed; reload will succeed without it.","Re-save the file ensuring a complete write (check disk space / editor behavior)."],"exampleFix":"// before: JSONC/trailing comma in workspace config\n{\"permissions\": {\"bash\": [\"git *\"],}}\n// after\n{\"permissions\": {\"bash\": [\"git *\"]}}","handlingStrategy":"validation","validationCode":"wsPath := filepath.Join(dataDir, \"crush.json\")\nif b, err := os.ReadFile(wsPath); err == nil && len(b) > 0 && !json.Valid(b) { fixOrRemove(wsPath) }","typeGuard":null,"tryCatchPattern":"if err := reload(); err != nil && strings.Contains(err.Error(), \"invalid JSON\") {\n    os.Remove(workspaceConfigPath); reload()\n}","preventionTips":["Never paste JSONC/YAML into .json files","Ensure atomic writes (write temp + rename) for generated configs","Validate with jq before saving"],"tags":["configuration","json","workspace"],"backgroundTag":"invalid-json-config","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}