plandex-ai/plandex · error

error writing hash file: %v

Error message

error writing hash file: %v

What it means

SaveCustomModelsHash writes the computed hash to <basePath>.hash (e.g. custom-models.json.hash) with os.WriteFile(..., 0644). This error is the OS-level write failure for the hash file; the hash itself was computed successfully. Consequence: custom-models.json may exist without a matching hash, causing subsequent syncs to treat local state as changed.

Source

Thrown at app/cli/lib/custom_models.go:172

	err = SaveCustomModelsHash(path, modelsInput)
	if err != nil {
		return fmt.Errorf("error saving hash file: %v", err)
	}

	return nil
}

func SaveCustomModelsHash(basePath string, modelsInput *shared.ModelsInput) error {
	hashPath := basePath + ".hash"

	hash, err := modelsInput.Hash()
	if err != nil {
		return fmt.Errorf("error hashing models: %v", err)
	}

	err = os.WriteFile(hashPath, []byte(hash), 0644)
	if err != nil {
		return fmt.Errorf("error writing hash file: %v", err)
	}

	return nil
}

func MustSyncCustomModels(path string, serverModelsInput *shared.ModelsInput) bool {
	term.StartSpinner("")

	jsonData, err := os.ReadFile(path)
	if err != nil {
		term.OutputErrorAndExit("Error reading custom models file: %v", err)
		return false
	}

	clientModelsInput, err := schema.ValidateModelsInputJSON(jsonData)
	if err != nil {
		term.StopSpinner()
		color.New(color.Bold, term.ColorHiRed).Println("🚨 Error validating custom models file")

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped error for 'permission denied' and correct ownership: chown -R $(whoami) ~/.plandex.
  2. Free disk space if the disk/quota is full.
  3. Remove a corrupt or root-owned custom-models.json.hash and retry the models command.
  4. Re-run the custom models write so the JSON file and hash are written together consistently.

Example fix

// before
-rw-r--r-- 1 root root custom-models.json.hash  # unwritable by user
// after
sudo rm ~/.plandex/accounts/<userId>/custom-models.json.hash
plandex models  # rewrites hash as current user
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the hash file location is writable before syncing
hp := path + ".hash"
if f, err := os.OpenFile(hp, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
    // abort: hash file not writable
} else {
    f.Close()
}

Type guard

func isHashWriteError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "error writing hash file")
}

Try / catch

if err := lib.WriteCustomModelsFile(path, input); err != nil {
    if isHashWriteError(err) {
        term.Error("Hash file write failed; delete " + path + ".hash and retry")
        return
    }
    return err
}

Prevention

When it happens

Trigger: os.WriteFile failing on the hash path because the directory is missing, permissions deny writing, or disk is full — same conditions as the JSON write but specifically on the .hash file.

Common situations: Read-only filesystem; quota exceeded; .hash file owned by root after sudo usage; accounts directory manually cleaned while file handle expectations remain.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/fee80efea20ab0f7. Report an issue: GitHub.