{"record":{"id":"01213562973b3389","repo":"plandex-ai/plandex","slug":"error-reading-hash-file-v","errorCode":null,"errorMessage":"error reading hash file: %v","messagePattern":"error reading hash file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/cli/lib/custom_models.go","lineNumber":121,"sourceCode":"\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 {\n\t\treturn CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error hashing models: %v\", err)\n\t}\n\n\treturn CustomModelsCheckLocalChangesResult{\n\t\tHasLocalChanges:  currentHash != string(lastSavedHash),\n\t\tLocalModelsInput: localModelsInput,\n\t}, nil\n}\n\nfunc WriteCustomModelsFile(path string, modelsInput *shared.ModelsInput) error {\n\terr := os.MkdirAll(filepath.Dir(path), 0755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating directory: %v\", err)\n\t}","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/cli/lib/custom_models.go#L103-L139","documentation":"CustomModelsCheckLocalChanges reads a companion hash file used to detect local modifications; any read error other than 'file does not exist' (which is tolerated) is wrapped as 'error reading hash file: %v'.","triggerScenarios":"os.ReadFile(hashPath) fails with a non-IsNotExist error — permission denied on the hash file, hashPath is a directory, or an I/O error while reading the stored hash.","commonSituations":"Permissions broken on the custom-models state directory; sync tool left the hash path as a directory; disk errors; hash file locked by another process.","solutions":["Check the wrapped OS error for the exact read failure reason","Fix permissions on the directory containing the hash file","Delete the hash file — missing hash is tolerated and treated as 'local changes', triggering a resync","Rerun the sync flow so the hash file is rewritten correctly"],"exampleFix":"// before\nlastSavedHash, err := os.ReadFile(hashPath)\nif err != nil && !os.IsNotExist(err) {\n    return CustomModelsCheckLocalChangesResult{}, fmt.Errorf(\"error reading hash file: %v\", err)\n}\n// after: caller preflight clears bad hash state\nif info, err := os.Stat(hashPath); err == nil && info.IsDir() {\n    os.RemoveAll(hashPath) // let sync recreate it\n}","handlingStrategy":"fallback","validationCode":"if info, err := os.Stat(hashPath); err == nil && info.IsDir() {\n    os.RemoveAll(hashPath)\n}","typeGuard":null,"tryCatchPattern":"res, err := CustomModelsCheckLocalChanges(...)\nif err != nil && strings.Contains(err.Error(), \"error reading hash file\") {\n    os.Remove(hashPath) // missing hash is tolerated; treat as local changes\n    return CustomModelsCheckLocalChanges(path, hashPath)\n}","preventionTips":["Delete (not preserve) corrupt hash files — absence is a supported state","Keep hash and JSON files written together atomically","Ensure no directory occupies the hash file path"],"tags":["filesystem","hash","custom-models","permissions"],"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"}