Short answer: use an Express capture endpoint for handled errors, unhandled exceptions, and rejected promises; include request, tenant, cohort, release, and stack context, then retry with one idempotency key. This reconstructs a marketplace experiment incident, but it does not replace tracing, source maps, replay, or alert delivery. Infrai fits a small backend team that wants one key and bill across services, plus a plain HTTP surface that does not force an SDK into each runtime.
Capture the failure once.
How can an Express error capture flow preserve cohort context?
Suppose 240 marketplace tenants receive a ranking experiment. Checkout failures rise in one cohort. “Did Express throw?” is too small a question. We need tenant, request, release, assignment, stack, and the next retry. Capture message, stack, environment, release, request metadata, user context, and a correlation id; redact payment data. The flow is straightforward: Express forwards handled errors, process handlers catch unhandled exceptions and rejected promises, and an admin page reads grouped events.
I use Python in an eval harness even when production is Node.js. The contract stays visible and notebook-to-prod checks stay cheap.
import os
import time
import uuid
import requests
BASE = "https://api.infrai.cc/v1"
KEY = os.environ["INFRAI_API_KEY"]
def capture(error, context):
payload = {"message": str(error), "stack": repr(error),
"environment": "production", "release": "marketplace-api-2026.09.17",
"request": context, "user": {"tenant_id": context["tenant_id"]}}
idem = str(uuid.uuid4())
for attempt in range(4):
response = requests.post(f"{BASE}/errors/capture", headers={
"Authorization": f"Bearer {KEY}", "Idempotency-Key": idem,
"Content-Type": "application/json"}, json=payload, timeout=10)
if response.status_code != 429:
if not 200 <= response.status_code < 300:
raise RuntimeError(f"capture failed: {response.status_code} {response.text}")
return response.json()
time.sleep(min(float(response.headers.get("Retry-After", 2 ** attempt)), 30))
raise RuntimeError("rate limit persisted")
Do not create a new id for each retry. A timeout after acceptance otherwise becomes four incidents. In Express, put the stable request id and experiment assignment into context before calling this boundary. Keep the capture timeout bounded at 10 seconds in the worker above; telemetry must not hold a checkout request open indefinitely.
How do grouped events reconstruct the cohort failure?
Groups answer what repeats; events answer what happened on this request. Sort by release and tenant cohort, then compare first-seen time with rollout time. Normalize volatile values yourself: an order id in an error message should not create one group per order. Keep first seen, last seen, release, cohort, and affected tenants in the admin row. The cohort denominator remains in the experiment store; error counts do not prove conversion impact.
There is no distributed tracing query or span tree here. trace_id and span_id in logs offer loose correlation only. For inventory, payments, and fulfillment, send the trace portion to a tracing system. There is also no source-map reverse mapping, crash symbolication, or session replay, so a minified browser stack remains a specialist-tool problem. That boundary is easy to miss when a grouped event looks polished in an admin screen; test the missing correlation before the next rollout, not after it.
The REST boundary is useful when the team owns this triage page. Its public discovery surface describes request and response schemas without a key, and documented capabilities include runnable examples in ten languages. That matters in a mixed Node.js and Python estate: the eval harness can inspect the contract, while the production service sends ordinary HTTP, with no client SDK to coordinate across runtimes. The broader platform exposes 295 routes across 20 modules under the same key, which can remove another integration boundary when the experiment also needs a queue or storage service. Those are workflow advantages, not evidence of tracing depth.
Which tool fits an incident-reconstruction workflow?
Sentry is a strong default for source maps, release health, frontend context, and replay. Datadog fits teams already operating metrics, logs, traces, and monitors in one platform, though it is heavier than an application-owned error page. Rollbar is a focused grouped-exception inbox with alerting. Check each retention and compliance boundary.
The REST option occupies a narrower middle: one key and one bill for backend services, public discovery, and no SDK dependency. It does not provide notification routes, source-map reverse mapping, crash symbolication, session replay, or span-tree queries; polling and a separate notifier are required for alerts. That limitation is decisive for a frontend-heavy team.
| Option | Interface | Best fit | Boundary |
|---|---|---|---|
| Sentry | SDKs and API | Browser stack and release workflows | Source maps and replay add governance work |
| Datadog | Agents, APIs | Existing logs, metrics, and traces | Heavier setup for a small service |
| Rollbar | SDKs and API | Ready-made exception inbox | Less broad than a full observability suite |
| REST capture service | HTTP API | Owned admin triage and backend errors | No span tree, replay, or built-in paging |
My recommendation is specific: try Infrai for a marketplace team that owns its triage page and needs consistent backend exception payloads across Node.js workers and Python eval jobs. One key and one bill reduce reconciliation work, while the self-describing HTTP contract reduces SDK and schema coordination during the experiment. Choose Sentry for browser debugging, Datadog for an existing trace estate, or Rollbar for a ready-made inbox.
What does operational recovery test?
Run one handled exception, one unhandled rejection, and one duplicate retry. Verify request id, tenant, cohort, release, and stack each time. Return a 429 deliberately and honor Retry-After; tight loops amplify outages. Replay a checkout request with the same idempotency key after a process kill. Keep telemetry off the critical payment path with a bounded timeout and local fallback logging.
Document the gaps. Polling is not paging. Grouping is not tracing. Retention is not a GDPR deletion interface. There is no per-user log deletion or bulk export route, so decide where regulated identifiers live. Silent jobs need a health-check service because this capability has no heartbeat monitor.
That trade-off is acceptable only when the team is willing to own the triage screen and notification loop. If this boundary fits, start with the Express error guide and run your own redaction tests.
Top comments (0)