siyuan-note/siyuan · error

RPC reply requires a response or error

Error message

RPC reply requires a response or error

What it means

pluginRPCReply converts an internal JsonRpcProcessingResponse into an apicontract.PluginRPCReply. A response must carry either a Result (success) or an Error; if response.Error is nil after the success path was not taken, the reply is unusable and this error is thrown.

Solutions

  1. Set response.Error to a JsonRpcProcessingError whenever the call did not succeed
  2. Fix the dispatch path so every response is either RPCSuccessReply or carries a populated Error
  3. Check that the success encoding path did not silently drop the response before reaching the error branch
  4. Add a pre-check validating Result-or-Error presence before calling pluginRPCReply

Example fix

// before
resp := JsonRpcProcessingResponse{}
reply, err := pluginRPCReply(resp)
// after
resp := JsonRpcProcessingResponse{Error: &JsonRpcProcessingError{Code: -32603, Message: "internal error"}}
reply, err := pluginRPCReply(resp)
Defensive patterns

Strategy: validation

Validate before calling

if resp.Result == nil && resp.Error == nil {
    return fmt.Errorf("response must carry result or error")
}

Type guard

func replyable(r JsonRpcProcessingResponse) bool { return r.Error != nil }

Try / catch

reply, err := pluginRPCReply(resp)
if err != nil {
    reply = apicontract.RPCFailureReply(id, -32603, err.Error())
}

Prevention

When it happens

Trigger: Calling pluginRPCReply with a JsonRpcProcessingResponse whose Result was not encodable into a success reply and whose Error field is nil — e.g. a zero-value response struct, or a handler that returned neither a result nor an error.

Common situations: Constructing JsonRpcProcessingResponse manually in tests; a dispatch path that forgets to set response.Error when the plugin call fails without producing a result; type changes in the response struct leaving fields unset.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/7100d2e49be5c440. Report an issue: GitHub.

Appendix: source

Thrown at kernel/plugin/rpc_contract.go:159

	if response.Response != nil {
		value := response.Response
		result, err := json.Marshal(value.Result)
		if err != nil {
			return apicontract.PluginRPCReply{}, err
		}
		encoded, err := apicontract.EncodedJSONValue(result)
		if err != nil {
			return apicontract.PluginRPCReply{}, err
		}
		id, err := pluginRPCID(value.ID)
		if err != nil {
			return apicontract.PluginRPCReply{}, err
		}
		return apicontract.RPCSuccessReply(apicontract.PluginRPCSuccess{JSONRPC: value.JsonRpc, Result: encoded, ID: id}), nil
	}
	value := response.Error
	if value == nil {
		return apicontract.PluginRPCReply{}, fmt.Errorf("RPC reply requires a response or error")
	}
	id, err := pluginRPCID(value.ID)
	if err != nil {
		return apicontract.PluginRPCReply{}, err
	}
	result := apicontract.PluginRPCFailure{JSONRPC: value.JsonRpc, ID: id}
	if value.Error != nil {
		result.Error = &apicontract.PluginRPCError{Code: int(value.Error.Code), Message: value.Error.Message}
		if value.Error.Data != nil {
			data, err := json.Marshal(value.Error.Data)
			if err != nil {
				return apicontract.PluginRPCReply{}, err
			}
			encoded, err := apicontract.EncodedJSONValue(data)
			if err != nil {
				return apicontract.PluginRPCReply{}, err
			}
			result.Error.Data = &encoded

View on GitHub (pinned to 9f775e8a12)