charmbracelet/crush · error

invalid hook configuration on reload: %w

Error message

invalid hook configuration on reload: %w

What it means

On reload, hooks defined in the reloaded config must be re-validated (matcher regexes recompiled) via cfg.ValidateHooks, mirroring initial Load. If validation fails, reload aborts with this wrapped error and the store rolls back, keeping the previously working hooks.

Source

Thrown at internal/config/store.go:1211

	// Merge workspace config if present
	workspacePath := filepath.Join(cfg.Options.DataDirectory, fmt.Sprintf("%s.json", appName))
	if wsData, err := os.ReadFile(workspacePath); err == nil && len(wsData) > 0 {
		if !json.Valid(wsData) {
			return fmt.Errorf("invalid JSON in config file %s", workspacePath)
		}
		merged, mergeErr := loadFromBytes(append([][]byte{mustMarshalConfig(cfg)}, wsData))
		if mergeErr == nil {
			dataDir := cfg.Options.DataDirectory
			*cfg = *merged
			cfg.setDefaults(s.workingDir, dataDir)
			loadedPaths = append(loadedPaths, workspacePath)
		}
	}

	// Validate hooks after all config merging is complete so matcher
	// regexes are recompiled on the reloaded config (mirrors Load).
	if err := cfg.ValidateHooks(); err != nil {
		return fmt.Errorf("invalid hook configuration on reload: %w", err)
	}

	// Save current state for potential rollback BEFORE configureProviders,
	// which may write to disk via RemoveConfigField (e.g. removing stale
	// OAuth providers). Capturing after would snapshot a config that has
	// already been mutated, and the rollback would restore corrupted state.
	oldConfig := s.Config()
	oldLoadedPaths := s.loadedPaths
	oldResolver := s.resolver
	oldKnownProviders := s.knownProviders
	oldOverrides := s.overrides
	oldWorkspacePath := s.workspacePath

	// Preserve runtime overrides
	overrides := s.overrides

	// Reapply model choices made in this instance. The global config file is
	// shared, so it may now name a model a sibling instance selected; a

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Fix the hook definition flagged by the wrapped error — usually an invalid matcher regex or missing command field.
  2. Test the regex in isolation (go playground or regex101 with RE2 syntax) before adding it to config.
  3. Roll back the recent hook edit and reload to restore the working state.
  4. Validate hooks locally via the config validation path before sharing the config.

Example fix

// before: invalid regex
hook pretooluse --matcher "(edit|write" --command ./check.sh
// after
hook pretooluse --matcher "(edit|write)" --command ./check.sh
Defensive patterns

Strategy: validation

Validate before calling

for _, h := range cfg.Hooks { if _, err := regexp.Compile(h.Matcher); err != nil { return err }; if h.Command == "" { return errors.New("empty hook command") } }

Try / catch

if err := reload(); err != nil && strings.Contains(err.Error(), "invalid hook configuration") {
    revertLastHookEdit(); reload()
}

Prevention

When it happens

Trigger: cfg.ValidateHooks() returns an error after merging: a hook with an invalid matcher regex, empty/missing command, or unsupported event name in the newly loaded crushrc/crush.json.

Common situations: User added a hook in the session directory with a bad regex like "(" or a typo'd event; a shared team config was updated with an incompatible hook schema version.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/5a0de138306d390b. Report an issue: GitHub.