Fix ArithmeticError in ServeShapePlug when parse_body halts before telemetry span#3929
Fix ArithmeticError in ServeShapePlug when parse_body halts before telemetry span#3929
Conversation
…meticError When parse_body halted a request before start_telemetry_span had executed, the custom halt/1 would call end_telemetry_span which tried to compute `System.monotonic_time() - nil`, causing an ArithmeticError. Fixes #3919 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3929 +/- ##
===========================================
+ Coverage 75.75% 87.21% +11.46%
===========================================
Files 11 25 +14
Lines 693 2394 +1701
Branches 174 600 +426
===========================================
+ Hits 525 2088 +1563
- Misses 167 304 +137
- Partials 1 2 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
These tests verify that POST requests with invalid JSON or non-object JSON bodies return proper 400 responses without crashing. Before the plug ordering fix, these paths would trigger an ArithmeticError in end_telemetry_span. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code ReviewSummaryThis PR fixes a real production What's Working Well
Issues FoundImportant (Should Fix)Two of four The linked issue (#3919) explicitly identifies the body-exceeds-size-limit path ( File: Suggested additions to the test "returns 413 for oversized body without crashing", ctx do
Repatch.patch(Plug.Conn, :read_body, fn conn, _opts -> {:more, "partial", conn} end)
conn =
Plug.Test.conn(:post, "/?offset=-1", "body")
|> put_req_header("content-type", "application/json")
|> call_serve_shape_plug(ctx)
assert conn.status == 413
assert %{"error" => "Request body too large"} = Jason.decode!(conn.resp_body)
end
test "returns 400 for body read failure without crashing", ctx do
Repatch.patch(Plug.Conn, :read_body, fn conn, _opts -> {:error, :timeout, conn} end)
conn =
Plug.Test.conn(:post, "/?offset=-1", "body")
|> put_req_header("content-type", "application/json")
|> call_serve_shape_plug(ctx)
assert conn.status == 400
assert %{"error" => "Failed to read request body"} = Jason.decode!(conn.resp_body)
endSuggestions (Nice to Have)1. Defensive nil-check in The plug ordering constraint is implicit and comment-only — a future developer could break the invariant again. Adding a guard in # packages/sync-service/lib/electric/plug/serve_shape_plug.ex:283
defp end_telemetry_span(%Conn{assigns: assigns} = conn, _ \\ nil) do
span = conn.private[:electric_telemetry_span]
OpenTelemetry.execute(
[:electric, :plug, :serve_shape],
%{
count: 1,
bytes: assigns[:streaming_bytes_sent] || 0,
monotonic_time: System.monotonic_time(),
duration: if span, do: System.monotonic_time() - span[:start_time], else: 0
},
...
)
...
endThis is a nice-to-have — the reordering fix is correct and sufficient on its own. 2. This is a pre-existing issue (not introduced by this PR), but worth noting: Issue ConformanceFully addresses #3919. The root cause analysis in the issue is accurate, and the PR's reordering approach directly eliminates the crash path. The PR description matches the implementation. The issue was well-specified with exact reproduction steps, affected versions, and the two commits responsible — nothing is left unaddressed. Review iteration: 1 | 2026-03-02 |
|
@claude Two things:
|
|
Claude finished @alco's task in 2m 21s —— View job Changes implemented
Commit: Summary of changes
plug :put_resp_content_type, "application/json" # ← moved here (was after parse_body)
plug :fetch_query_params
plug :start_telemetry_span
plug :parse_body
|
✅ Deploy Preview for electric-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Ensures all error responses (including those from parse_body) carry the correct Content-Type: application/json header. Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1beba88 to
9725a13
Compare
|
This PR has been released! 🚀 The following packages include changes from this PR:
Thanks for contributing to Electric! |
Summary
parse_bodyplug afterstart_telemetry_spanin the pipeline to preventArithmeticErrorwhenparse_bodyhalts (e.g. oversized body, bad JSON) before the telemetry span is initializedhalt/1callsend_telemetry_span()which tried to computeSystem.monotonic_time() - nil, crashing the requestFixes #3919
Test plan
serve_shape_plug_test.exstests pass (31 tests, 0 failures)🤖 Generated with Claude Code