fix(webapp): log transient Attio 5xx/429 at warn instead of error (#4270)

The signup → Attio sync (`attio.server.ts` `#assert`) logged every
non-2xx response at `error` level and threw the same way regardless of
status. Transient upstream failures (5xx/429) are retried by the common
worker and self-heal, so treating them as errors created false alerts
for something that isn't actually a bug.

Now `#assert` splits the two cases:

- **5xx / 429** — Logged at `warn` and thrown with `logLevel: "warn"`,
so they continue to be retried but don't raise error-level alerts. This
reuses the same pattern the worker already honors
(`directorySyncEffects`).
- **4xx** — Unchanged: logged at `error` and thrown, so genuine
integration bugs (schema, permissions, auth, etc.) remain visible.

There is no behavior change to retries or the signup flow. This is a
server-only change.

---------

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Iss
2026-07-15 10:57:39 -04:00
committed by GitHub
parent 890dd66eb5
commit 80cbc46bf6
2 changed files with 18 additions and 7 deletions
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---
Transient internal sync failures are now retried quietly instead of surfacing as errors.
+12 -7
View File
@@ -47,13 +47,18 @@ class AttioClient {
if (!response.ok) {
const body = await response.text();
logger.error("Attio assert failed", {
object,
matchingAttribute,
status: response.status,
body,
});
throw new Error(`Attio assert ${object} failed with status ${response.status}`);
// 5xx/429 are transient (the worker retries); warn + tag so they don't page Sentry. Real 4xx stay error.
const transient = response.status >= 500 || response.status === 429;
const fields = { object, matchingAttribute, status: response.status, body };
if (transient) {
logger.warn("Attio assert failed", fields);
} else {
logger.error("Attio assert failed", fields);
}
const message = `Attio assert ${object} failed with status ${response.status}`;
throw transient
? Object.assign(new Error(message), { logLevel: "warn" as const })
: new Error(message);
}
const recordId = ((await response.json()) as any).data?.id?.record_id;