Skip to content

Improves RPC request deserialisation error messaging#851

Merged
3alpha merged 6 commits intomainfrom
rpc-deserialisation-error-handling
Sep 19, 2025
Merged

Improves RPC request deserialisation error messaging#851
3alpha merged 6 commits intomainfrom
rpc-deserialisation-error-handling

Conversation

@3alpha
Copy link
Copy Markdown
Collaborator

@3alpha 3alpha commented Sep 17, 2025

Usage related changes

Improves RPC request deserialisation handling, brings it closer to the client implementations.

Development related changes

N/A

Checklist:

I still need to add tests.

  • Checked out the contribution guidelines
  • Applied formatting - ./scripts/format.sh
  • No linter errors - ./scripts/clippy_check.sh
  • No unused dependencies - ./scripts/check_unused_deps.sh
  • No spelling errors - ./scripts/check_spelling.sh
  • Performed code self-review
  • Rebased to the latest commit of the target branch (or merged it into my branch)
    • Once you make the PR reviewable, please avoid force-pushing
  • Updated the docs if needed - ./website/README.md
  • Linked the issues resolvable by this PR - linking info
  • Updated the tests if needed; all passing - execution info

Summary by CodeRabbit

  • Bug Fixes

    • Standardized JSON-RPC error responses with correct codes, messages, and “reason” details.
    • Improved handling for malformed JSON, invalid requests, and missing Content-Type; responses now use proper JSON-RPC error objects and id null when unknown.
    • WebSocket now returns proper JSON-RPC parse errors for non-JSON messages and cleans up connections more reliably.
  • Tests

    • Added integration tests for HTTP /rpc error scenarios (syntax errors, invalid requests, missing Content-Type).
    • Added WebSocket test validating parse-error responses for malformed payloads.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 17, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

  • Adjusted WebSocket JSON-RPC error handling to return a JSON-RPC error object (using rpc_core::request::Id::Null) instead of a plain string, and made socket map cleanup unconditional after the message loop.
  • Enhanced HTTP /rpc handler to map axum JsonRejection variants to specific RpcError constructors: parse_error with a reason for syntax errors and invalid_request_with_reason for data/content-type/other issues.
  • Updated rpc_core::error: changed parse_error to accept a reason and added invalid_request_with_reason; both include a {"reason": "..."} data field.
  • Added integration tests covering HTTP parse errors, invalid requests, missing content type, and WebSocket parse errors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Refactor server crate #847 — Also modifies JSON-RPC handling and rpc_core::error, indicating overlapping changes to error construction and response behavior.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive The description includes the required template sections (Usage, Development, Checklist) and gives a brief summary, but it contains a contradictory note ("I still need to add tests.") while the checklist marks tests as updated and passing, and it lacks concrete details about user‑facing behavior, affected endpoints, and references to the added tests. Please clarify whether tests were added and their status (fix the contradictory checklist entry), expand "Usage related changes" with specific effects and any compatibility concerns, and link or reference the new tests so reviewers can validate the described behavior.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely and accurately summarizes the primary change—improving RPC request deserialization error messaging—which matches the PR's modifications to JSON‑RPC error handling, handler logic, and tests; it is short, specific, and free of noisy details.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@3alpha 3alpha changed the title Return parse error on deserialization Improves RPC request deserialisation error messaging Sep 17, 2025
@3alpha 3alpha marked this pull request as ready for review September 17, 2025 19:11
@3alpha
Copy link
Copy Markdown
Collaborator Author

3alpha commented Sep 17, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 17, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
crates/starknet-devnet-server/src/rpc_core/error.rs (1)

46-56: Docstring nit: say “with reason”, not “with a message”.

The helper preserves the standard message and places details in data.reason. Update the comment to avoid confusion.

-// Creates a new `InvalidRequest` with a message
+// Creates a new `InvalidRequest` with a reason in `data.reason`
crates/starknet-devnet-server/src/rpc_handler.rs (1)

70-74: Avoid leaking internal rejection details to clients (optional).

err.to_string() in the fallback can expose framework internals. Consider returning invalid_request() without a reason, and keep the detailed warn! log.

-                warn!(target: "rpc", "Request rejection: {}", err);
-                Response::error(RpcError::invalid_request_with_reason(err.to_string())).into()
+                warn!(target: "rpc", "Request rejection: {}", err);
+                Response::error(RpcError::invalid_request()).into()
tests/integration/test_websocket.rs (1)

113-132: Make the test robust and check send result.

  • Assert ws.send(...) succeeds (currently ignored).
  • Avoid strict equality on the entire JSON; assert structure/fields to prevent brittleness if serde error wording changes subtly.
-    let _ =
-        ws.send(tokio_tungstenite::tungstenite::Message::Text("garbage_string".to_string())).await;
-
-    let resp_raw = ws.next().await.unwrap().unwrap();
-    let resp: serde_json::Value = serde_json::from_slice(&resp_raw.into_data()).unwrap();
-    assert_eq!(
-        resp,
-        json!({
-            "jsonrpc": "2.0",
-            "id": serde_json::Value::Null,
-            "error": { "code": -32700, "message": "Parse error", "data": {"reason" : "expected value at line 1 column 1"} }
-        })
-    );
+    ws.send(tokio_tungstenite::tungstenite::Message::Text("garbage_string".to_string()))
+        .await
+        .expect("ws send failed");
+
+    let resp_raw = ws.next().await.unwrap().unwrap();
+    let resp: serde_json::Value = serde_json::from_slice(&resp_raw.into_data()).unwrap();
+    assert_eq!(resp["jsonrpc"], "2.0");
+    assert_eq!(resp["id"], serde_json::Value::Null);
+    assert_eq!(resp["error"]["code"], -32700);
+    assert_eq!(resp["error"]["message"], "Parse error");
+    assert!(resp["error"]["data"]["reason"]
+        .as_str()
+        .unwrap()
+        .contains("expected value"));
crates/starknet-devnet-server/src/api/json_rpc_handler.rs (2)

36-36: Import nit: narrow the import to Id if that’s all you need.

Minor readability: import Id directly instead of the whole rpc_core for this use.

-use crate::{ServerConfig, rpc_core};
+use crate::ServerConfig;
+use crate::rpc_core::request::Id;

Then:

-                rpc_core::request::Id::Null
+                Id::Null

535-540: Tiny optimization: avoid json! round-trip when serializing.

Serialize the struct directly; it skips the intermediate Value.

-            Err(e) => json!(RpcResponse::from_rpc_error(
-                RpcError::parse_error(e.to_string()),
-                rpc_core::request::Id::Null
-            ))
-            .to_string(),
+            Err(e) => serde_json::to_string(&RpcResponse::from_rpc_error(
+                RpcError::parse_error(e.to_string()),
+                Id::Null
+            ))
+            .expect("serialize RpcResponse"),
tests/integration/general_rpc_tests.rs (1)

128-157: Add HTTP 200 assert for consistency.

The other tests assert status; add it here too.

     let resp = reqwest::Client::new()
         .post(format!("{}/rpc", devnet.url))
         .header("Content-Type", "application/json")
         .json(&json!({
             "jsonrpc": "2.0",
             "params": [],
             "id": 1
         }))
         .send()
         .await
         .unwrap();
 
+    assert_eq!(resp.status(), reqwest::StatusCode::OK);
     let error_resp: serde_json::Value = resp.json().await.unwrap();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3fed84 and 4851f73.

📒 Files selected for processing (5)
  • crates/starknet-devnet-server/src/api/json_rpc_handler.rs (3 hunks)
  • crates/starknet-devnet-server/src/rpc_core/error.rs (3 hunks)
  • crates/starknet-devnet-server/src/rpc_handler.rs (1 hunks)
  • tests/integration/general_rpc_tests.rs (1 hunks)
  • tests/integration/test_websocket.rs (2 hunks)
🔇 Additional comments (5)
crates/starknet-devnet-server/src/rpc_core/error.rs (1)

26-35: Breaking change: parse_error now requires a reason; confirm semver/consumers.

Changing pub const fn parse_error() -> Self to pub fn parse_error<R>(reason: R) -> Self is a public API break. Ensure all internal/external callers are updated and bump the crate version accordingly if published.

tests/integration/test_websocket.rs (1)

3-3: LGTM: imports required for ws read/write.

crates/starknet-devnet-server/src/api/json_rpc_handler.rs (1)

144-144: Unconditional socket cleanup: good reliability improvement.

Removing socket_id on all exit paths prevents leaked entries. Nice.

tests/integration/general_rpc_tests.rs (2)

96-126: Relies on exact axum error text; server code should normalize reason.

Current server logic splits by “ at line”, which still leaves details like “: expected value…”. After normalizing the server to a constant reason (see rpc_handler.rs suggestion), this test will pass and remain stable across axum/serde versions.


159-187: LGTM: covers missing content type path and expected shape.

@3alpha 3alpha requested a review from FabijanC September 18, 2025 13:37
@3alpha 3alpha force-pushed the rpc-deserialisation-error-handling branch from 97a9879 to 1a91dd0 Compare September 18, 2025 15:16
@3alpha 3alpha merged commit 302faf5 into main Sep 19, 2025
2 checks passed
@3alpha 3alpha deleted the rpc-deserialisation-error-handling branch September 19, 2025 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants