plandex-ai/plandex · error
error reading JSON file: %v
Error message
error reading JSON file: %v
What it means
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'.
Source
Thrown at app/cli/lib/custom_models.go:107
return serverModelsInput, nil
}
func CustomModelsCheckLocalChanges(path string) (CustomModelsCheckLocalChangesResult, error) {
hashPath := path + ".hash"
exists, err := fs.FileExists(path)
if err != nil {
return CustomModelsCheckLocalChangesResult{}, err
}
if !exists {
return CustomModelsCheckLocalChangesResult{}, nil
}
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 {View on GitHub (pinned to e2d772072e)
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
Example fix
// before
localJsonData, err := os.ReadFile(path)
if err != nil {
return CustomModelsCheckLocalChangesResult{}, fmt.Errorf("error reading JSON file: %v", err)
}
// after: caller preflight
if info, err := os.Stat(path); err != nil || info.IsDir() {
return CustomModelsCheckLocalChangesResult{}, nil // treat as missing, resync
} Defensive patterns
Strategy: validation
Validate before calling
if info, err := os.Stat(path); err != nil || info.IsDir() {
return CustomModelsCheckLocalChangesResult{}, nil // treat as absent, resync
} Type guard
func fileReadableRegular(path string) bool {
info, err := os.Stat(path)
return err == nil && info.Mode().IsRegular()
} Try / catch
res, err := CustomModelsCheckLocalChanges(...)
if err != nil && strings.Contains(err.Error(), "error reading JSON file") {
os.Remove(path) // unreadable stale file; let sync recreate it
return SyncCustomModels()
} Prevention
- Avoid hand-editing generated custom-models.json
- Serialize writes to the file to avoid read/delete races
- Keep state directory permissions consistent
When it happens
Trigger: 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.
Common situations: 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.
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 loading accounts: %v
- error getting parent project ids with paths: %v
- error getting child project ids with paths: %v
- file does not exist: %s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/14f6f66d9e2c8b7a.
Report an issue: GitHub.