router-for-me/CLIProxyAPI · error
plugin executor %s count tokens panic: %v
Error message
plugin executor %s count tokens panic: %v
What it means
The plugin's CountTokens implementation panicked; the adapter's deferred recover() converted the panic to an error, zeroed the response, and fused the plugin via fusePlugin so subsequent calls fail fast.
Source
Thrown at internal/pluginhost/adapters_executors.go:769
if next == nil {
return nil, fmt.Errorf("plugin executor %s refresh returned invalid auth data", a.Identifier())
}
if auth != nil {
next.CreatedAt = auth.CreatedAt
next.UpdatedAt = auth.UpdatedAt
}
return next, nil
}
func (a *executorAdapter) CountTokens(ctx context.Context, auth *coreauth.Auth, req coreexecutor.Request, opts coreexecutor.Options) (resp coreexecutor.Response, err error) {
if a == nil || a.executor == nil || a.host.isPluginFused(a.pluginID) || !a.host.pluginIdentityCurrent(a.pluginID, a.path, a.version) {
return coreexecutor.Response{}, fmt.Errorf("plugin executor %s is unavailable", a.Identifier())
}
defer func() {
if recovered := recover(); recovered != nil {
a.host.fusePlugin(a.pluginID, "Executor.CountTokens", recovered)
resp = coreexecutor.Response{}
err = fmt.Errorf("plugin executor %s count tokens panic: %v", a.Identifier(), recovered)
}
}()
prepared, errPrepare := a.prepareExecutorCall(req, opts)
if errPrepare != nil {
return coreexecutor.Response{}, errPrepare
}
pluginResp, errCountTokens := a.executor.CountTokens(ctx, buildExecutorRequest(a.host, a.provider, auth, prepared.req, prepared.opts))
if errCountTokens != nil {
return coreexecutor.Response{}, errCountTokens
}
return coreexecutor.Response{
Payload: a.translateExecutorResponse(ctx, prepared, pluginResp.Payload, false, nil),
Metadata: cloneAnyMap(pluginResp.Metadata),
Headers: cloneHeader(pluginResp.Headers),
}, nil
}
View on GitHub (pinned to 78f0c4079e)
Solutions
- Read the recovered panic value in the host log to locate the failing dereference in the plugin
- Make the plugin's CountTokens defensive: validate model/payload before use and return an error instead of panicking
- Reinstall the fixed plugin and reload so the fuse clears
- Ensure the models the plugin advertises match the models its tokenizer actually supports
Defensive patterns
Strategy: try-catch
Try / catch
resp, err := adapter.CountTokens(ctx, auth, req, opts)
if err != nil {
if strings.Contains(err.Error(), "count tokens panic") {
return fallbackCountTokens(req) // plugin fused; use local estimator
}
return coreexecutor.Response{}, err
} Prevention
- Test plugin tokenizers against every model the plugin advertises
- Return errors from plugins instead of panicking; wrap plugin code in its own recover during development
- Alert on 'count tokens panic' and remove the model from routing until fixed
When it happens
Trigger: Calling CountTokens where the plugin's CountTokens dereferences something invalid — e.g. unexpected model name, missing tokenizer entry, nil request payload fields after prepareExecutorCall.
Common situations: Plugin tokenizer lookup table lacks the requested model; plugin assumes a payload schema the host no longer sends; plugin built against an older plugin API.
Related errors
- plugin executor %s stream panic: %v
- plugin executor %s http request panic: %v
- plugin executor %s refresh panic: %v
- auth provider panic: %v
- plugin executor %s is unavailable
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/21097b61aab7f25d.
Report an issue: GitHub.