router-for-me/CLIProxyAPI · error

pluginhost: token store unavailable

Error message

pluginhost: token store unavailable

What it means

When a plugin's command-line execution returns auth data to persist, the host looks up the SDK token store via sdkAuth.GetTokenStore(). If no token store has been initialized in the process, persistence is aborted with this error and the plugin's returned credentials are not saved.

Source

Thrown at internal/pluginhost/command_line.go:371

		return pluginapi.CommandLineExecutionResponse{}, nil
	}
	defer func() {
		if recovered := recover(); recovered != nil {
			h.fusePlugin(record.id, "CommandLinePlugin.ExecuteCommandLine", recovered)
			resp = pluginapi.CommandLineExecutionResponse{}
			err = fmt.Errorf("command-line execution panic: %v", recovered)
		}
	}()
	return plugin.ExecuteCommandLine(ctx, req)
}

func (h *Host) persistCommandLineAuths(ctx context.Context, auths []pluginapi.AuthData) ([]string, error) {
	if len(auths) == 0 {
		return nil, nil
	}
	store := sdkAuth.GetTokenStore()
	if store == nil {
		return nil, fmt.Errorf("pluginhost: token store unavailable")
	}
	summary := h.hostConfigSummary()
	if summary.AuthDir != "" {
		if setter, okSetter := store.(interface{ SetBaseDir(string) }); okSetter {
			setter.SetBaseDir(summary.AuthDir)
		}
	}
	savedPaths := make([]string, 0, len(auths))
	for index, authData := range auths {
		record := h.AuthDataToCoreAuth(authData, "", "")
		if record == nil {
			return savedPaths, fmt.Errorf("pluginhost: command-line auth %d is invalid", index+1)
		}
		savedPath, errSave := store.Save(ctx, record)
		if errSave != nil {
			return savedPaths, fmt.Errorf("pluginhost: save command-line auth %s: %w", record.ID, errSave)
		}
		if strings.TrimSpace(savedPath) != "" {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Initialize the token store before running command-line plugin flows (set it up via the SDK's builder/service initialization so GetTokenStore returns non-nil).
  2. If embedding, follow the standard service bootstrap in sdk/cliproxy rather than constructing the plugin host standalone.
  3. In tests, install an in-memory token store before invoking command-line execution.

Example fix

// before: host runs command-line flow, store never initialized
resp, err := host.ExecuteCommandLine(ctx, req)
// persistCommandLineAuths -> "pluginhost: token store unavailable"

// after: initialize the store during service bootstrap
store := filestore.New(authDir)
sdkAuth.SetTokenStore(store) // or equivalent builder option
resp, err := host.ExecuteCommandLine(ctx, req)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the token store exists before running command-line flows
if sdkAuth.GetTokenStore() == nil {
    return fmt.Errorf("token store not initialized; initialize auth storage before plugin command-line flows")
}

Type guard

func tokenStoreReady() bool {
    return sdkAuth.GetTokenStore() != nil
}

Try / catch

paths, err := host.PersistCommandLineAuths(ctx, auths)
if err != nil && strings.Contains(err.Error(), "token store unavailable") {
    // Non-retryable setup error: initialize store, then re-run the whole command-line flow
    return initializeStoreAndRetry()
}

Prevention

When it happens

Trigger: Running a plugin command-line flow (plugin.ExecuteCommandLine returning non-empty auths) in a context where the SDK token store was never initialized — e.g. a minimal embedding of the SDK that skipped token store setup, or a host started in a mode that does not register a store.

Common situations: Embedding sdk/cliproxy in a custom binary without configuring auth persistence; running management/command-line tooling before the service fully initializes; a test harness that spins up the plugin host without the auth store.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/ffdcb48e9dc5986f. Report an issue: GitHub.