Plugin JsonRpcResponse uses id: u64, so plugin error responses with id: null (parse errors, invalid requests) fail deserialization and hang the caller
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 75/100
Research direction
Read src-tauri/src/plugins/rpc.rs and the response handling in driver.rs around lines 262–275. Check how JsonRpcResponse is deserialized and how pending requests are resolved; run the relevant plugin RPC tests, if available. Done means responses with id: null deserialize without leaving a caller waiting, while normal response handling still works.
Written by the indexing model from the issue text.
Description
Problem
The plugin host's JsonRpcResponse type deserializes the id field as
u64 (not nullable), but the JSON-RPC 2.0 spec requires id: null in error
responses when the request id can't be determined (parse errors -32700,
invalid requests -32600). When a plugin sends such a response, the host
fails to deserialize it, logs "Failed to parse plugin response", discards the
line, and the pending request's oneshot::Sender is never resolved — the
caller hangs until the call timeout fires.
Cause
JsonRpcResponse in src-tauri/src/plugins/rpc.rs (lines 41-54):
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum JsonRpcResponse {
Success {
jsonrpc: String,
result: Value,
id: u64,
},
Error {
jsonrpc: String,
error: JsonRpcError,
id: u64,
},
}
Both variants use id: u64. When the plugin sends {"jsonrpc":"2.0", "error":{"code":-32700,"message":"parse error: ..."},"id":null} (or
-32600), serde_json::from_str::<JsonRpcResponse> fails because null
is not a valid u64. The #[serde(untagged)] enum tries Success (no
result field → fails) then Error (null is not u64 → fails), returns
Err, and the host discards the line at driver.rs:273-275:
Err(e) => {
log::error!("Failed to parse plugin response: {}", e);
}
The pending_requests map still holds the caller's oneshot::Sender, so the
caller hangs until the call timeout fires — the same hang the plugin's #135
fix was meant to prevent.
When this triggers
- Parse error (
-32700): the plugin sendsid: nullfor any line that
isn't valid JSON (e.g. a garbled stdin line). Pre-existing — this path has
always sentid: null. - Invalid request (
-32600): the plugin sendsid: nullfor a non-object
JSON-RPC line (array, number, string, bool, null). Added by
tabularis-postgresql-plugin#135 (PR #136). - Any future plugin error response where the id is undeterminable.
In all cases the plugin is spec-correct; the host can't consume the response.
Suggested fix
Change id to Option<u64> (or serde_json::Value) in both variants:
pub enum JsonRpcResponse {
Success {
jsonrpc: String,
result: Value,
id: Option<u64>,
},
Error {
jsonrpc: String,
error: JsonRpcError,
id: Option<u64>,
},
}
Then update the two match arms in driver.rs (lines 263-271) to handle
None — there's no pending request to match (the request id couldn't be
determined), so log and discard, which is already what happens when
pending_requests.remove(&id) finds nothing:
Ok(JsonRpcResponse::Success { result, id, .. }) => {
if let Some(id) = id {
if let Some(tx) = pending_requests.remove(&id) {
let _ = tx.send(Ok(result));
}
}
}
Ok(JsonRpcResponse::Error { error, id, .. }) => {
if let Some(id) = id {
if let Some(tx) = pending_requests.remove(&id) {
let _ = tx.send(Err(PluginCallError::Remote(error)));
}
}
}
This is a one-file change in src-tauri/src/plugins/rpc.rs plus the two match
arms in driver.rs. The JsonRpcResponse type is only consumed by
driver.rs (the MCP JsonRpcResponse in src-tauri/src/mcp/protocol.rs is a
separate struct, not affected).
Scope
This is the host-side companion to tabularis-postgresql-plugin#135/#137. The
plugin correctly sends id: null per JSON-RPC 2.0; the host just can't
deserialize it. Confirmed by reading the source: id: u64 in rpc.rs:47,52,
deserialization at driver.rs:262, discard at driver.rs:273-275.
Surfaced by /code-review on tabularis-postgresql-plugin PR #136.
- Dominant language
- TypeScript
- Stars
- 5.1k
- Forks
- 335
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 75
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TabularisDB/tabularis
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
TabularisDB/tabularis#905 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
TabularisDB/tabularis#903 ·
Maintainers usually reply within 1 day
-
[Bug]: Plugin UI slots data-grid.toolbar.actions and sidebar.footer.actions receive an empty contextOpenbug help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
TabularisDB/tabularis#892 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
TabularisDB/tabularis#919 · 3 comments ·
Maintainers usually reply within 1 day
-
[Bug]: "Export Logs" says "Logs exported to clipboard" but writes a filePossibly taken @tosinxt claimed this today. Openbug
TabularisDB/tabularis#907 · 2 comments · 1 assignee ·
Maintainers usually reply within 1 day
All issues in TabularisDB/tabularis
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
lukilabs/beautiful-mermaid#160 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
rescript-lang/rescript-lang.org#1420 ·
Maintainers usually reply within 2 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
chthollyphile/folia-major#520 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
databuddy-analytics/Databuddy#1106 ·
Maintainers usually reply within 1 day