{"record":{"id":"14f6f66d9e2c8b7a","repo":"plandex-ai/plandex","slug":"error-reading-json-file-v","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/custom_models.go","lineNumber":107,"sourceCode":"\n\treturn serverModelsInput, nil\n}\n\nfunc CustomModelsCheckLocalChanges(path string) (CustomModelsCheckLocalChangesResult, error) {\n\thashPath := path + \".hash\"\n\n\texists, err := fs.FileExists(path)\n\tif err != nil {\n\t\treturn CustomModelsCheckLocalChangesResult{}, err\n\t}\n\n\tif !exists {\n\t\treturn CustomModelsCheckLocalChangesResult{}, nil\n\t}\n\n\tlocalJsonData, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error reading JSON file: %v\", err)\n\t}\n\n\tvar localClientModelsInput shared.ClientModelsInput\n\terr = json.Unmarshal(localJsonData, &localClientModelsInput)\n\tif err != nil {\n\t\treturn CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error unmarshalling JSON file: %v\", err)\n\t}\n\n\tlocalModelsInput := localClientModelsInput.ToModelsInput()\n\n\tlastSavedHash, err := os.ReadFile(hashPath)\n\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error reading hash file: %v\", err)\n\t}\n\n\tcurrentHash, err := localModelsInput.Hash()\n\tif err != nil {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/custom_models.go#L89-L125","documentation":"CustomModelsCheckLocalChanges reads the local custom-models JSON file after confirming it exists; a read failure (not absence — absence returns an empty result) is wrapped as 'error reading JSON file: %v'.","triggerScenarios":"os.ReadFile(path) fails on the local custom-models JSON that os.Stat already reported as existing — race where the file was deleted between stat and read, permission denied, or the path is a directory.","commonSituations":"Permissions changed on the models JSON between checks; another process (editor/sync tool) deleted or locked the file concurrently; file became a directory; disk I/O error.","solutions":["Check the wrapped OS error (%v) for the exact read failure","Verify the file's permissions and that it is a regular file","Delete the stale file and let the CLI regenerate it (SyncCustomModels rewrites it)","Rerun the sync flow to recreate custom-models.json from server state"],"exampleFix":"// before\nlocalJsonData, err := os.ReadFile(path)\nif err != nil {\n    return CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error reading JSON file: %v\", err)\n}\n// after: caller preflight\nif info, err := os.Stat(path); err != nil || info.IsDir() {\n    return CustomModelsCheckLocalChangesResult{}, nil // treat as missing, resync\n}","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(path); err != nil || info.IsDir() {\n    return CustomModelsCheckLocalChangesResult{}, nil // treat as absent, resync\n}","typeGuard":"func fileReadableRegular(path string) bool {\n    info, err := os.Stat(path)\n    return err == nil && info.Mode().IsRegular()\n}","tryCatchPattern":"res, err := CustomModelsCheckLocalChanges(...)\nif err != nil && strings.Contains(err.Error(), \"error reading JSON file\") {\n    os.Remove(path) // unreadable stale file; let sync recreate it\n    return SyncCustomModels()\n}","preventionTips":["Avoid hand-editing generated custom-models.json","Serialize writes to the file to avoid read/delete races","Keep state directory permissions consistent"],"tags":["filesystem","json","custom-models","cli"],"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"}