{"record":{"id":"64f10be84c6ba056","repo":"plandex-ai/plandex","slug":"error-reading-json-file-v-64f10b","errorCode":null,"errorMessage":"error reading JSON file: %v","messagePattern":"error reading JSON file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/model_settings.go","lineNumber":51,"sourceCode":"\thashPath := path + \".hash\"\n\n\texists, err := fs.FileExists(path)\n\tif err != nil {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, fmt.Errorf(\"error checking model settings file: %v\", err)\n\t}\n\n\tif !exists {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, nil\n\t}\n\n\tlastSavedHash, err := os.ReadFile(hashPath)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, fmt.Errorf(\"error reading hash file: %v\", err)\n\t}\n\n\tlocalJsonData, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, fmt.Errorf(\"error reading JSON file: %v\", err)\n\t}\n\n\tvar clientModelPackSchemaRoles *shared.ClientModelPackSchemaRoles\n\terr = json.Unmarshal(localJsonData, &clientModelPackSchemaRoles)\n\tif err != nil {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, fmt.Errorf(\"error unmarshalling JSON file: %v\", err)\n\t}\n\n\tcurrentHash, err := clientModelPackSchemaRoles.ToModelPackSchemaRoles().Hash()\n\tif err != nil {\n\t\treturn ModelSettingsCheckLocalChangesResult{}, fmt.Errorf(\"error hashing model pack: %v\", err)\n\t}\n\n\tmodelPackSchemaRoles := clientModelPackSchemaRoles.ToModelPackSchemaRoles()\n\n\treturn ModelSettingsCheckLocalChangesResult{\n\t\tHasLocalChanges:           currentHash != string(lastSavedHash),\n\t\tLocalModelPackSchemaRoles: &modelPackSchemaRoles,","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/model_settings.go#L33-L69","documentation":"After the hash check, ModelSettingsCheckLocalChanges reads the model settings JSON file itself. Unlike the hash file, a missing settings file is NOT tolerated here — any os.ReadFile error is wrapped with this message, because the file was already confirmed to exist by the earlier fs.FileExists check.","triggerScenarios":"os.ReadFile(path) fails after fs.FileExists(path) returned true — a race where the file was deleted between the stat and the read, permission denied, or I/O error.","commonSituations":"Another process (editor, sync tool, git checkout) deletes/replaces the settings file concurrently; the file exists but was chmod'd unreadable; symlink to an unreadable target.","solutions":["Re-run the check — if it was a delete race, the file will either exist again or be reported as absent.","Check file permissions (chmod u+r) on the settings file.","Avoid running file-sync tools that delete/replace the settings file while the CLI is running.","If the path is a symlink, verify the target is readable."],"exampleFix":"// before: blind read\n// after: caller-side existence check to shrink the race window\nif _, err := os.Stat(settingsPath); err != nil {\n    return nil\n}\nres, err := ModelSettingsCheckLocalChanges(settingsPath)","handlingStrategy":"retry","validationCode":"if _, err := os.Stat(settingsPath); err != nil {\n    return nil\n}\nif f, err := os.Open(settingsPath); err != nil {\n    return fmt.Errorf(\"settings file unreadable: %w\", err)\n} else {\n    f.Close()\n}","typeGuard":null,"tryCatchPattern":"res, err := ModelSettingsCheckLocalChanges(settingsPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"reading JSON file\") {\n        time.Sleep(100 * time.Millisecond)\n        res, err = ModelSettingsCheckLocalChanges(settingsPath)\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Don't run git checkout/sync tools that delete settings files while the CLI runs.","Keep the settings file chmod u+r for the running user.","Prefer atomic writes (temp file + rename) for the settings file to avoid partial states.","Retry once on read errors — transient races resolve themselves."],"tags":["filesystem","json","race-condition"],"backgroundTag":"file-read-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}