router-for-me/CLIProxyAPI · error

plugin call %s returned %d: %s

Error message

plugin call %s returned %d: %s

What it means

The native plugin entry point (invoked via cgo cliproxy_call_plugin) returned a nonzero return code, and the response body was not a recognized plugin error envelope. The message embeds the RPC method name, the numeric rc, and whatever bytes the plugin wrote to the response buffer. This is the generic failure path for unix dynamic-library plugins.

Source

Thrown at internal/pluginhost/loader_unix.go:197

	var cRequest unsafe.Pointer
	if len(request) > 0 {
		cRequest = C.CBytes(request)
		defer C.free(cRequest)
	}
	var response C.cliproxy_buffer
	rc := C.cliproxy_call_plugin(c.api.call, cMethod, (*C.uint8_t)(cRequest), C.size_t(len(request)), &response)
	var out []byte
	if response.ptr != nil && response.len > 0 {
		out = C.GoBytes(response.ptr, C.int(response.len))
	}
	if response.ptr != nil {
		C.cliproxy_free_plugin_buffer(c.api.free_buffer, response.ptr, response.len)
	}
	if rc != 0 {
		if isPluginErrorEnvelope(out) {
			return out, nil
		}
		return nil, fmt.Errorf("plugin call %s returned %d: %s", method, int(rc), string(out))
	}
	return out, nil
}

func (c *dynamicLibraryClient) Shutdown() {
	if c == nil {
		return
	}
	if c.api.shutdown != nil {
		C.cliproxy_shutdown_plugin(c.api.shutdown)
		c.api.shutdown = nil
	}
	if c.hostCtx != nil {
		id := uintptr(*(*C.uintptr_t)(c.hostCtx))
		hostCallbackEntries.Delete(id)
		C.free(c.hostCtx)
		c.hostCtx = nil
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Check the plugin's own logs/stderr for the real cause; the %s payload usually carries the plugin's error text
  2. Verify the method name string matches exactly what the plugin registers
  3. Rebuild the plugin against the same pluginhost SDK/ABI version as the host binary
  4. If you author the plugin, return errors as the SDK's error envelope so the host surfaces them as structured responses instead of this opaque failure

Example fix

// before
resp, err := client.Call(ctx, "transform.Request", body)
if err != nil { log.Fatal(err) }

// after
resp, err := client.Call(ctx, "transform.Request", body)
if err != nil {
    log.Errorf("plugin rpc failed (method=%s): %v", "transform.Request", err)
    return fallbackResponse // degrade gracefully instead of crashing
}
Defensive patterns

Strategy: fallback

Try / catch

out, err := client.Call(ctx, method, body)
if err != nil {
    log.WithError(err).WithField("method", method).Error("plugin rpc failed")
    return nil, err // or serve degraded response

Prevention

When it happens

Trigger: Plugin method implementation returns an error without serializing it as a plugin error envelope; calling a method name the plugin does not implement; plugin crashes or corrupts its own state inside call(); ABI drift where the plugin misinterprets arguments.

Common situations: Plugin built against an older SDK version that formats errors differently; method name typo in the host-side dispatch (e.g. camelCase vs snake_case); plugin panicking across the C boundary and returning garbage; plugin out-of-memory returning rc != 0 with empty output.

Related errors


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