router-for-me/CLIProxyAPI · error
plugin call %s returned %d: %s
Error message
plugin call %s returned %d: %s
What it means
Windows version of the generic plugin-call failure: the plugin's call function returned a nonzero rc via syscall.SyscallN, and the output bytes were not a recognized plugin error envelope. The message includes the method name, the numeric return code, and the raw output string. The host frees the plugin's response buffer before reporting, so no leak occurs.
Source
Thrown at internal/pluginhost/loader_windows.go:305
c.api.call,
uintptr(unsafe.Pointer(methodBytes)),
requestPtr,
uintptr(len(request)),
responseMem,
)
var out []byte
if response.ptr != 0 && response.len > 0 {
out = unsafe.Slice((*byte)(unsafe.Pointer(response.ptr)), response.len)
out = append([]byte(nil), out...)
}
if response.ptr != 0 {
_, _, _ = syscall.SyscallN(c.api.freeBuffer, response.ptr, response.len)
}
if rc != 0 {
if isPluginErrorEnvelope(out) {
return out, nil
}
return nil, fmt.Errorf("plugin call %s returned %d: %s", method, rc, string(out))
}
return out, nil
}
func (c *dynamicLibraryClient) Shutdown() {
// Windows Go DLLs are not safe to hot-unload from the host process.
// The plugin was loaded from a shadow copy, so keeping the module mapped
// does not block deleting or replacing the source artifact.
c.close(false)
}
func (c *dynamicLibraryClient) closeAfterOpenFailure() {
c.close(true)
}
func (c *dynamicLibraryClient) close(releaseDLL bool) {
if c == nil {
returnView on GitHub (pinned to 78f0c4079e)
Solutions
- Read the %s output — most plugins put their error text there
- Confirm the method name matches the plugin's registered handlers exactly
- Rebuild the plugin against the same pluginhost SDK version as the server
- Reproduce with a minimal request payload to find the input the plugin chokes on
Defensive patterns
Strategy: fallback
Try / catch
out, err := client.Call(ctx, method, body)
if err != nil {
log.WithError(err).WithField("method", method).Error("windows plugin rpc failed")
return degradedResponse, nil // keep request alive if a fallback exists
} Prevention
- Pin plugin and host to the same SDK version
- Log method names on every dispatch
- Test plugins with edge-case payloads before release
When it happens
Trigger: Plugin method returns an error without the SDK error envelope; unknown method name dispatched to the DLL; plugin internal panic or abort returning nonzero; ABI-mismatched argument marshalling.
Common situations: Plugin and host built from different SDK revisions; method-name typos in custom dispatch glue; plugin crashing on malformed input bytes.
Related errors
- plugin call %s returned %d: %s
- cliproxy_plugin_init returned %d: %v
- plugin ABI version %d is not supported
- plugin function table is incomplete
- remove stale shadow plugin: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/aac3dafac759865b.
Report an issue: GitHub.