plandex-ai/plandex · error
error reading hash file: %v
Error message
error reading hash file: %v
What it means
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'.
Source
Thrown at app/cli/lib/custom_models.go:121
}
localJsonData, err := os.ReadFile(path)
if err != nil {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error reading JSON file: %v", err)
}
var localClientModelsInput shared.ClientModelsInput
err = json.Unmarshal(localJsonData, &localClientModelsInput)
if err != nil {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error unmarshalling JSON file: %v", err)
}
localModelsInput := localClientModelsInput.ToModelsInput()
lastSavedHash, err := os.ReadFile(hashPath)
if err != nil && !os.IsNotExist(err) {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error reading hash file: %v", err)
}
currentHash, err := localModelsInput.Hash()
if err != nil {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error hashing models: %v", err)
}
return CustomModelsCheckLocalChangesResult{
HasLocalChanges: currentHash != string(lastSavedHash),
LocalModelsInput: localModelsInput,
}, nil
}
func WriteCustomModelsFile(path string, modelsInput *shared.ModelsInput) error {
err := os.MkdirAll(filepath.Dir(path), 0755)
if err != nil {
return fmt.Errorf("error creating directory: %v", err)
}View on GitHub (pinned to e2d772072e)
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
Example fix
// before
lastSavedHash, err := os.ReadFile(hashPath)
if err != nil && !os.IsNotExist(err) {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error reading hash file: %v", err)
}
// after: caller preflight clears bad hash state
if info, err := os.Stat(hashPath); err == nil && info.IsDir() {
os.RemoveAll(hashPath) // let sync recreate it
} Defensive patterns
Strategy: fallback
Validate before calling
if info, err := os.Stat(hashPath); err == nil && info.IsDir() {
os.RemoveAll(hashPath)
} Try / catch
res, err := CustomModelsCheckLocalChanges(...)
if err != nil && strings.Contains(err.Error(), "error reading hash file") {
os.Remove(hashPath) // missing hash is tolerated; treat as local changes
return CustomModelsCheckLocalChanges(path, hashPath)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- error creating directory: %v
- error writing file: %v
- error saving hash file: %v
- error writing hash file: %v
- error reading hash file: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/01213562973b3389.
Report an issue: GitHub.