electric
electric copied to clipboard
feat: Add structured error codes for stack unavailability
Summary
Adds a machine-readable error code to all 503 responses when the Electric stack cannot respond to requests. This enables clients to programmatically detect stack unavailability without parsing error message strings.
Changes
- New Module:
Electric.Shapes.Api.ErrorCodewith singleSTACK_UNAVAILABLEerror code - Updated Response: Added
error_codefield to Response struct - Updated StatusMonitor: Now returns structured error information with both message and error code
- Updated API error handling: All stack unavailability errors now include error code and are marked as known errors
- Simple tests: Test file covering error code functionality
Error Response Format
Before
{
"message": "Timeout waiting for database connection pool (snapshot) to be ready"
}
After
{
"message": "Timeout waiting for database connection pool (snapshot) to be ready",
"code": "STACK_UNAVAILABLE"
}
Design Decisions
- Single error code: All stack unavailability scenarios use the same
STACK_UNAVAILABLEcode. The message already provides component-specific details. - No retry metadata: Retry timing is handled by the
RETRY-AFTERheader (being added in another PR). The error code is only for high-level categorization. - Simple and clean: Just adds a
codefield - no additional metadata likecomponent,retryable, orbackoff_ms.
Benefits
For Clients
- Programmatic detection: Reliably detect stack unavailability without string parsing
- Better error handling: Can differentiate between stack unavailable (503 with code) vs other errors (503 without code)
- Future-proof: Easy to add more error codes for other scenarios (client errors, etc.)
For Operations
- Better monitoring: Track stack unavailability errors separately from other 503s
- Cleaner metrics: Count
STACK_UNAVAILABLEerrors as a single category
For Support
- Clearer bug reports: Users can provide error code
- Consistent categorization: All stack unavailability issues have the same code
Example Client Usage
async function fetchShape(url: string): Promise<Response> {
const response = await fetch(url);
if (response.status === 503) {
const error = await response.json();
if (error.code === 'STACK_UNAVAILABLE') {
// Stack is unavailable - check RETRY-AFTER header for retry timing
const retryAfter = response.headers.get('retry-after');
console.log(`Stack unavailable: ${error.message}`);
console.log(`Retry after: ${retryAfter}`);
// Implement retry logic based on RETRY-AFTER header
// ...
}
}
return response;
}
Backwards Compatibility
✅ Fully backwards compatible - all changes are additive:
- Error messages unchanged
- HTTP status codes unchanged
- Existing
timeout_message/1function preserved - Error code is an additional field
Clients that don't check for error codes will continue to work using HTTP status codes and message parsing.
Testing
- All existing tests pass
- New test file covers error code functionality
- Updated existing test to verify error code presence in responses
- Verified error code appears in all stack unavailability scenarios
Test Plan
- [x] Verify
STACK_UNAVAILABLEcode is returned in all stack 503 responses - [x] Verify error code field is only added when error_code is provided
- [x] Test StatusMonitor timeout error detection
- [x] Verify backwards compatibility (existing tests still pass)
- [ ] Manual testing of various stack unavailability scenarios (can be done during review/staging)
🤖 Generated with Claude Code
Codecov Report
:x: Patch coverage is 63.15789% with 7 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 69.60%. Comparing base (ec24e64) to head (69882e1).
:warning: Report is 76 commits behind head on main.
| Files with missing lines | Patch % | Lines |
|---|---|---|
| ...ckages/sync-service/lib/electric/status_monitor.ex | 0.00% | 7 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## main #3357 +/- ##
==========================================
+ Coverage 67.00% 69.60% +2.60%
==========================================
Files 169 179 +10
Lines 8786 9726 +940
Branches 106 334 +228
==========================================
+ Hits 5887 6770 +883
- Misses 2898 2954 +56
- Partials 1 2 +1
| Flag | Coverage Δ | |
|---|---|---|
| elixir | 66.69% <63.15%> (-0.03%) |
:arrow_down: |
| elixir-client | 74.47% <ø> (+0.52%) |
:arrow_up: |
| packages/experimental | 87.73% <ø> (ø) |
|
| packages/react-hooks | 86.48% <ø> (ø) |
|
| packages/typescript-client | 94.41% <ø> (?) |
|
| packages/y-electric | 55.12% <ø> (ø) |
|
| postgres-140000 | 65.67% <63.15%> (-0.21%) |
:arrow_down: |
| postgres-150000 | 65.73% <63.15%> (-0.14%) |
:arrow_down: |
| postgres-170000 | 65.81% <63.15%> (+0.05%) |
:arrow_up: |
| postgres-180000 | 65.68% <63.15%> (-0.07%) |
:arrow_down: |
| sync-service | 65.92% <63.15%> (-0.08%) |
:arrow_down: |
| typescript | 87.32% <ø> (+14.92%) |
:arrow_up: |
| unit-tests | 69.60% <63.15%> (+2.60%) |
:arrow_up: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
Deploy Preview for electric-next ready!
| Name | Link |
|---|---|
| Latest commit | 69882e1bf385126eee9e53d4d7e63289a79702d0 |
| Latest deploy log | https://app.netlify.com/projects/electric-next/deploys/69034dcf7ea5820008605f58 |
| Deploy Preview | https://deploy-preview-3357--electric-next.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify project configuration.
Closing this PR and converting it to issue #3469 for further discussion and implementation planning.