router-for-me/CLIProxyAPI · error
cliproxy_plugin_init returned %d: %v
Error message
cliproxy_plugin_init returned %d: %v
What it means
On Windows, the host resolves and calls the cliproxy_plugin_init export in the plugin DLL via syscall; a nonzero return code triggers this error. It means the plugin's initialization function itself failed (the errCall component is the syscall.LastError, usually not the plugin's reason). The client is closed via closeAfterOpenFailure.
Source
Thrown at internal/pluginhost/loader_windows.go:105
id := windowsHostCallbackID.Add(1)
hostCtx := new(uintptr)
*hostCtx = id
windowsHostCallbackEntries.Store(id, dynamicHostCallbackEntry{host: host, pluginID: file.ID})
client := &dynamicLibraryClient{
dll: dll,
tempPath: loadPath,
hostCtx: hostCtx,
hostAPI: &windowsHostAPI{
abiVersion: pluginHostABIVersion,
hostCtx: uintptr(unsafe.Pointer(hostCtx)),
call: windowsHostCallCallback,
freeBuffer: windowsHostFreeCallback,
},
}
rc, _, errCall := proc.Call(uintptr(unsafe.Pointer(client.hostAPI)), uintptr(unsafe.Pointer(&client.api)))
if rc != 0 {
client.closeAfterOpenFailure()
return nil, fmt.Errorf("cliproxy_plugin_init returned %d: %v", rc, errCall)
}
if client.api.abiVersion != pluginHostABIVersion {
client.closeAfterOpenFailure()
return nil, fmt.Errorf("plugin ABI version %d is not supported", client.api.abiVersion)
}
if client.api.call == 0 || client.api.freeBuffer == 0 {
client.closeAfterOpenFailure()
return nil, fmt.Errorf("plugin function table is incomplete")
}
return client, nil
}
func shadowCopyPlugin(file pluginFile) (string, error) {
dir, errDir := shadowPluginDir()
if errDir != nil {
return "", errDir
}
shadowPluginCleanupOnce.Do(func() {View on GitHub (pinned to 78f0c4079e)
Solutions
- Test the DLL in isolation (rundll32 or a small harness) to see whether cliproxy_plugin_init succeeds and which dependency fails
- Rebuild the plugin with the same toolchain/architecture (GOARCH amd64 vs 386) as the host process
- Place dependent DLLs alongside the original plugin artifact or link the plugin statically
- Rebuild the plugin against the pluginhost SDK version matching the server
Defensive patterns
Strategy: try-catch
Try / catch
client, err := loader.Open(file, host)
if err != nil {
log.WithError(err).WithField("plugin", file.Path).Error("plugin init failed")
continue // skip plugin, keep server up
} Prevention
- Verify DLL exports with dumpbin /exports before deploying
- Keep the plugin's runtime dependencies beside the artifact
- Match host and plugin architecture (amd64/386)
When it happens
Trigger: DLL's init returns nonzero because of missing runtime dependencies (VC++ redistributable), failed internal initialization, architecture mismatch (32-bit DLL in 64-bit host), or the DLL not actually exporting a working cliproxy_plugin_init.
Common situations: Plugin compiled with a different toolchain than expected; dependent DLLs not next to the shadow-copied plugin; plugin built for a different ABI of the plugin SDK whose init rejects the host API struct.
Related errors
- plugin function table is incomplete
- plugin ABI version %d is not supported
- remove stale shadow plugin: %w
- plugin client is closed
- plugin call %s returned %d: %s
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/f8960e51d51c1cb1.
Report an issue: GitHub.