router-for-me/CLIProxyAPI · error

decode host log request: %w

Error message

decode host log request: %w

What it means

callHostLog failed to json.Unmarshal the RPC payload from the plugin into rpcHostLogRequest. This is a protocol-level failure: the plugin sent a host.log callback message whose JSON does not match the expected request shape (wrong field types, unknown structure, truncated bytes).

Source

Thrown at internal/pluginhost/host_callbacks.go:325

}

func modelExecutionError(errMsg *interfaces.ErrorMessage) error {
	if errMsg == nil {
		return nil
	}
	if errMsg.Error != nil {
		return errMsg.Error
	}
	if errMsg.StatusCode > 0 {
		return fmt.Errorf("model execution failed with status %d", errMsg.StatusCode)
	}
	return fmt.Errorf("model execution failed")
}

func (h *Host) callHostLog(ctx context.Context, request []byte) ([]byte, error) {
	var req rpcHostLogRequest
	if errUnmarshal := json.Unmarshal(request, &req); errUnmarshal != nil {
		return nil, fmt.Errorf("decode host log request: %w", errUnmarshal)
	}
	ctx = h.resolveCallbackContext(req.HostCallbackID, ctx)
	message := strings.TrimSpace(req.Message)
	if message == "" {
		message = "plugin log"
	}
	fields := log.Fields{}
	for key, value := range req.Fields {
		key = strings.TrimSpace(key)
		if key != "" {
			fields[key] = value
		}
	}
	if requestID := logging.GetRequestID(ctx); requestID != "" {
		fields["request_id"] = requestID
	}
	entry := log.WithFields(fields)
	switch strings.ToLower(strings.TrimSpace(req.Level)) {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Rebuild/update the plugin against the same pluginhost/pluginapi version as the host so the log request schema matches.
  2. Inspect the wrapped json error (it names the field and offset) to see which key mismatches.
  3. In plugin code, only send simple types (string keys, scalar values) in Fields to stay schema-compatible.
  4. If framing corruption is suspected (random offsets), check the plugin transport logs for truncated messages.

Example fix

// plugin side, before
fields := map[string]any{"count": 42, "nested": struct{ A int }{1}} // unsupported shape
host.Log("msg", fields)

// after
fields := map[string]any{"count": 42} // scalars only
host.Log("msg", fields)
Defensive patterns

Strategy: validation

Validate before calling

// Plugin side: keep log fields to scalars and validate before send
fields := map[string]any{}
for k, v := range raw {
    switch v.(type) {
    case string, float64, bool, nil:
        fields[k] = v
    default:
        fields[k] = fmt.Sprintf("%v", v) // stringify anything else
    }
}

Type guard

func logFieldsRPCSafe(fields map[string]any) bool {
    for k, v := range fields {
        if strings.TrimSpace(k) == "" {
            return false
        }
        switch v.(type) {
        case string, float64, bool, nil:
        default:
            return false
        }
    }
    return true
}

Try / catch

if err := host.Log(message, fields); err != nil {
    if strings.Contains(err.Error(), "decode host log request") {
        // schema mismatch: fall back to plain message with no fields
        _ = host.Log(message, nil)
    }
}

Prevention

When it happens

Trigger: A plugin's host.log callback with mismatched JSON: Fields values of unexpected types, Message not a string, or a plugin built against a different rpcHostLogRequest schema. The error wraps the encoding/json failure with its position.

Common situations: Plugin and host compiled against different pluginhost RPC versions; a plugin marshaling a struct with incompatible field types; corrupted IPC framing producing invalid JSON.

Related errors


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