plandex-ai/plandex · error

error saving hash file: %v

Error message

error saving hash file: %v

What it means

After writing custom-models.json, WriteCustomModelsFile calls SaveCustomModelsHash to persist a hash of the models input alongside it. This error means SaveCustomModelsHash returned an error — either hashing the models input failed or writing the <path>.hash file failed — so the models file exists but its hash cache is missing or stale.

Source

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

		return fmt.Errorf("error creating directory: %v", err)
	}

	clientModelsInput := modelsInput.ToClientModelsInput()
	clientModelsInput.PrepareUpdate()

	jsonData, err := json.MarshalIndent(clientModelsInput, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling models: %v", err)
	}

	err = os.WriteFile(path, jsonData, 0644)
	if err != nil {
		return fmt.Errorf("error writing file: %v", err)
	}

	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)
	}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the wrapped %v error to distinguish hashing failure from hash-file write failure.
  2. Fix underlying filesystem issues (permissions, disk space) on ~/.plandex/accounts/<userId>/.
  3. Delete the stale custom-models.json.hash and re-run the models command so the hash is regenerated.
  4. Re-sync custom models (plandex models / SyncCustomModels) to rewrite both file and hash consistently.

Example fix

// before
rm ~/.plandex/accounts/<userId>/custom-models.json.hash  # stale hash
// after
plandex models  # rewrites custom-models.json and regenerates the .hash file
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that the models file and hash directory are writable
info, err := os.Stat(hashPath)
_ = info
if err != nil && !os.IsNotExist(err) {
    // handle: hash path inaccessible
}

Type guard

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

Try / catch

if err := lib.WriteCustomModelsFile(path, input); err != nil {
    if isHashSaveError(err) {
        os.Remove(path + ".hash") // clear stale hash
        term.Error("Models written but hash missing; re-run sync")
        return
    }
    return err
}

Prevention

When it happens

Trigger: modelsInput.Hash() failing, or os.WriteFile(hashPath, ...) failing due to the same directory permission/disk issues as the JSON write (custom_models.go:163-172).

Common situations: Disk full or read-only home dir striking between the two writes; a Hash() implementation failure from a schema version mismatch; hash file locked or owned by another user after sudo usage.

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/8894979ded5307ae. Report an issue: GitHub.