charmbracelet/crush · error

failed to reload config: %w

Error message

failed to reload config: %w

What it means

reloadFromDiskLocked calls loadFromConfigPaths then loadFromConfigPaths-merged loading of crushrc/crush.json files; if parsing/merging of the discovered config files fails, the error is wrapped as 'failed to reload config'. The store rolls back to its previous state.

Source

Thrown at internal/config/store.go:1183

// Concurrent calls are serialised via writeMu.
func (s *ConfigStore) ReloadFromDisk(ctx context.Context) error {
	if s.workingDir == "" {
		return fmt.Errorf("cannot reload: working directory not set")
	}
	s.writeMu.Lock()
	defer s.writeMu.Unlock()
	return s.reloadFromDiskLocked(ctx)
}

// reloadFromDiskLocked performs the actual reload. Caller must hold writeMu.
func (s *ConfigStore) reloadFromDiskLocked(ctx context.Context) error {
	// Migrate deprecated disable_notifications before reloading config.
	migrateDisableNotifications()

	configPaths := lookupConfigs(s.workingDir)
	cfg, loadedPaths, err := loadFromConfigPaths(ctx, configPaths)
	if err != nil {
		return fmt.Errorf("failed to reload config: %w", err)
	}

	// Apply defaults (using existing data directory if set)
	var dataDir string
	if cur := s.Config(); cur != nil && cur.Options != nil {
		dataDir = cur.Options.DataDirectory
	}
	cfg.setDefaults(s.workingDir, dataDir)

	// 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

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Validate the JSON (jq . crush.json) or bash syntax (bash -n crushrc) in the files listed by lookupConfigs for the working directory.
  2. Fix the syntax error at the reported position and reload again.
  3. Temporarily move the offending config file aside to confirm it is the culprit, then fix and restore.
  4. Check file permissions are readable by the process.

Example fix

// before: broken crush.json
{"providers": "oops"}
// after
{"providers": {"anthropic": {"api_key": "sk-..."}}}
Defensive patterns

Strategy: validation

Validate before calling

for _, p := range configPaths { if strings.HasSuffix(p, ".json") { b, _ := os.ReadFile(p); if len(b) > 0 && !json.Valid(b) { return fmt.Errorf("invalid JSON: %s", p) } } }

Try / catch

if err := store.ReloadFromDisk(ctx); err != nil {
    var cfgErr *ConfigLoadError
    if errors.As(err, &cfgErr) { openEditor(cfgErr.Path) }
}

Prevention

When it happens

Trigger: loadFromConfigPaths(ctx, configPaths) returns an error: malformed crushrc script or crush.json (invalid JSON, bad bash syntax in shell config), unreadable file, or a merge conflict in config structure.

Common situations: User just edited crush.json and left a syntax error; a crushrc with a failing command; permissions changed on a config file mid-session.

Related errors


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