From 80cbc46bf6f8dd4177398503e9d7885d351f7730 Mon Sep 17 00:00:00 2001 From: Iss <74388823+isshaddad@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:57:39 -0400 Subject: [PATCH] fix(webapp): log transient Attio 5xx/429 at warn instead of error (#4270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .server-changes/attio-sync-transient-retry.md | 6 ++++++ apps/webapp/app/services/attio.server.ts | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 .server-changes/attio-sync-transient-retry.md diff --git a/.server-changes/attio-sync-transient-retry.md b/.server-changes/attio-sync-transient-retry.md new file mode 100644 index 000000000..9c54416a4 --- /dev/null +++ b/.server-changes/attio-sync-transient-retry.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Transient internal sync failures are now retried quietly instead of surfacing as errors. diff --git a/apps/webapp/app/services/attio.server.ts b/apps/webapp/app/services/attio.server.ts index c26eca8e3..f0852509f 100644 --- a/apps/webapp/app/services/attio.server.ts +++ b/apps/webapp/app/services/attio.server.ts @@ -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;