packages/typescript-client/skills/electric-debugging/SKILL.md
Troubleshoot Electric sync issues. Covers fast-loop detection from CDN/proxy cache key misconfiguration, stale cache diagnosis (StaleCacheError), MissingHeadersError from CORS misconfiguration, 409 shape expired handling, SSE proxy buffering (nginx proxy_buffering off, Caddy flush_interval -1), HTTP/1.1 6-connection limit in local dev (Caddy HTTP/2 proxy), WAL growth from replication slots (max_slot_wal_keep_size), Vercel CDN cache issues, and onError/backoff behavior. Load when shapes are not receiving updates, sync is slow, or errors appear in the console.
npx skillsauth add electric-sql/electric electric-debuggingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill builds on electric-shapes and electric-proxy-auth. Read those first.
Enable debug logging to see retry and state machine behavior:
import { ShapeStream, FetchError } from '@electric-sql/client'
const stream = new ShapeStream({
url: '/api/todos',
backoffOptions: {
initialDelay: 1000,
maxDelay: 32000,
multiplier: 2,
debug: true, // Logs retry attempts
},
onError: (error) => {
if (error instanceof FetchError) {
console.error(`Sync error: ${error.status} at ${error.url}`, error.json)
}
return {} // Always return {} to retry
},
})
| Error | Auto-retry? | Action |
| --------------------- | -------------------------- | ------------------------------------------------------------- |
| 5xx server errors | Yes (exponential backoff) | Wait and retry |
| 429 rate limit | Yes (respects Retry-After) | Wait and retry |
| Network errors | Yes (exponential backoff) | Wait and retry |
| 4xx (non-429) | No | Calls onError — return {} to retry manually |
| 409 shape expired | Yes (automatic reset) | Client resets and refetches |
| MissingHeadersError | Never | Fix CORS/proxy — not retryable even if onError returns {} |
This error means Electric response headers (electric-offset, electric-handle) are being stripped, usually by CORS:
MissingHeadersError: This is often due to a proxy not setting CORS correctly
so that all Electric headers can be read by the client.
Fix: expose Electric headers in proxy CORS configuration:
headers.set(
'Access-Control-Expose-Headers',
'electric-offset, electric-handle, electric-schema, electric-cursor'
)
Console message: "Detected possible fast loop" with diagnostic info.
Cause: proxy/CDN cache key doesn't include handle and offset query params, so the client gets the same stale response repeatedly.
Fix: ensure your proxy/CDN includes all query parameters in its cache key.
For Vercel, add to vercel.json:
{
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "CDN-Cache-Control", "value": "no-store" },
{ "key": "Vercel-CDN-Cache-Control", "value": "no-store" }
]
}
]
}
Wrong:
# nginx caching without query params in key
proxy_cache_key $scheme$host$uri;
Correct:
# Include query params (handle, offset) in cache key
proxy_cache_key $scheme$host$request_uri;
Fast-loop detection fires after 5 requests in 500ms at the same offset. The client auto-clears caches once, then applies backoff, then throws after 5 consecutive detections.
Source: packages/typescript-client/src/client.ts:929-1002
Wrong:
location /v1/shape {
proxy_pass http://electric:3000;
# Default: proxy_buffering on — SSE responses delayed
}
Correct:
location /v1/shape {
proxy_pass http://electric:3000;
proxy_buffering off;
}
For Caddy:
reverse_proxy localhost:3000 {
flush_interval -1
}
Nginx and Caddy buffer responses by default, causing long delays for SSE live updates. Disable buffering for Electric endpoints. Do NOT disable caching entirely — Electric uses cache headers for request collapsing.
Source: website/docs/guides/troubleshooting.md:69-109
Wrong:
# Running Electric directly on localhost:3000
# With 7+ shapes, browser HTTP/1.1 queues all requests (6 connection limit)
Correct:
# Run Caddy as HTTP/2 proxy on host (not in Docker — Docker prevents HTTP/2)
caddy run --config - --adapter caddyfile <<EOF
localhost:3001 {
reverse_proxy localhost:3000
}
EOF
Browser HTTP/1.1 limits to 6 TCP connections per origin. With many shapes, requests queue behind each other. Use Caddy as a local HTTP/2 proxy.
Source: website/docs/guides/troubleshooting.md:28-53
Wrong:
docker stop electric
# Replication slot retains WAL indefinitely — disk fills up
Correct:
docker stop electric
# Drop slot when stopping for extended periods
psql -c "SELECT pg_drop_replication_slot('electric_slot_default');"
# Or set a safety limit
psql -c "ALTER SYSTEM SET max_slot_wal_keep_size = '10GB';"
psql -c "SELECT pg_reload_conf();"
Replication slots retain WAL indefinitely when Electric is disconnected. Postgres disk fills up. Either drop the slot or set max_slot_wal_keep_size.
Source: website/docs/guides/troubleshooting.md:203-316
See also: electric-deployment/SKILL.md — Many sync issues stem from deployment configuration. See also: electric-shapes/SKILL.md — onError semantics and backoff behavior.
Targets @electric-sql/client v1.5.10.
development
Interactive blog post authoring. Produces a draft blog post file with structured outline, inline guidance comments, and meta briefs that the author proses up in place. Supports pyramid principle, best sales deck, and release post formats.
development
Set up ElectricProvider for real-time collaborative editing with Yjs via Electric shapes. Covers ElectricProvider configuration, document updates shape with BYTEA parser (parseToDecoder), awareness shape at offset='now', LocalStorageResumeStateProvider for reconnection with stableStateVector diff, debounceMs for batching writes, sendUrl PUT endpoint, required Postgres schema (ydoc_update and ydoc_awareness tables), CORS header exposure, and sendErrorRetryHandler. Load when implementing collaborative editing with Yjs and Electric.
tools
Configure ShapeStream and Shape to sync a Postgres table to the client. Covers ShapeStreamOptions (url, table, where, columns, replica, offset, handle), custom type parsers (timestamptz, jsonb, int8), column mappers (snakeCamelMapper, createColumnMapper), onError retry semantics, backoff options, log modes (full, changes_only), requestSnapshot, fetchSnapshot, subscribe/unsubscribe, and Shape materialized view. Load when setting up sync, configuring shapes, parsing types, or handling sync errors.
documentation
Design Postgres schema and Electric shape definitions together for a new feature. Covers single-table shape constraint, cross-table joins using multiple shapes, WHERE clause design for tenant isolation, column selection for bandwidth optimization, replica mode choice (default vs full for old_value), enum casting in WHERE clauses, and txid handshake setup with pg_current_xact_id() for optimistic writes. Load when designing database tables for use with Electric shapes.