charmbracelet/crush · error

no variable resolver configured

Error message

no variable resolver configured

What it means

Thrown by ConfigStore.Resolve when no variable resolver has been configured on the store. The Resolve method only reads s.resolver under a read lock; if it is nil, there is no way to expand the variable reference, so it returns this error instead of panicking.

Source

Thrown at internal/config/store.go:172

// WorkingDir returns the current working directory.
func (s *ConfigStore) WorkingDir() string {
	return s.workingDir
}

// Resolver returns the variable resolver.
func (s *ConfigStore) Resolver() VariableResolver {
	s.writeMu.RLock()
	defer s.writeMu.RUnlock()
	return s.resolver
}

// Resolve resolves a variable reference using the configured resolver.
func (s *ConfigStore) Resolve(key string) (string, error) {
	s.writeMu.RLock()
	r := s.resolver
	s.writeMu.RUnlock()
	if r == nil {
		return "", fmt.Errorf("no variable resolver configured")
	}
	return r.ResolveValue(key)
}

// KnownProviders returns the list of known providers.
func (s *ConfigStore) KnownProviders() []catwalk.Provider {
	s.writeMu.RLock()
	defer s.writeMu.RUnlock()
	return s.knownProviders
}

// RefetchHyperProvider re-fetches the Hyper provider catalog from the
// remote API and updates the in-memory known providers list and config.
// This is called after OAuth authentication completes so the latest
// models are available without restarting.
func (s *ConfigStore) RefetchHyperProvider(ctx context.Context) error {
	// Build a fresh client that reads the API key from the live config,
	// not the stale snapshot captured at startup. The syncer's original

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Construct the ConfigStore with a resolver configured (the shell variable resolver used in production wiring).
  2. Check for init-order bugs where Resolve is called before the resolver is set.
  3. In tests, install a stub resolver so Resolve has a target.
  4. Guard callers: treat a nil resolver as a configuration bug and fail fast with a clear message.

Example fix

// before
store := config.NewConfigStore(...) // no resolver
store.Resolve("API_KEY") // error
// after
store := config.NewConfigStore(...)
store.SetResolver(shellVariableResolver)
store.Resolve("API_KEY")
Defensive patterns

Strategy: try-catch

Validate before calling

if store == nil || store.Resolver() == nil {
    return errors.New("config store not fully initialized")
}

Try / catch

val, err := store.Resolve(key)
if err != nil && strings.Contains(err.Error(), "no variable resolver configured") {
    return fmt.Errorf("cannot expand %q: resolver missing; check store initialization", key)
}

Prevention

When it happens

Trigger: Calling ConfigStore.Resolve(key) (e.g. via buildProvider or refreshApiKeyTemplate) on a store built without a resolver — a store constructed with the zero value or without the resolver option, often in tests or partial setups.

Common situations: Using ConfigStore programmatically without wiring shellVariableResolver; test fixtures omitting the resolver; initialization order bug where Resolve runs before the resolver is attached.

Related errors


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