Skip to main content

Bug fixes

Dead exception guards around the company domains PATCH (issue #888) set_company_domain_if_empty and its enrichment caller each guarded the domains PATCH with except AttioValidationError — a repo-local type the Attio SDK never raises. The live PATCH raises an SDKError-typed ResponseValidationError, so the intended domain_invalid classification never fired: a domain the server rejected (an RFC-2606 reserved TLD like acme.test, which passes the client-side hostname-shape check) surfaced as action="failed" instead of unresolved. The real 400 body was captured live against the dev workspace: Attio rejects such values with code: "validation_type" and a message naming slug "domains" — a code outside the SDK endpoint’s Literal union, which is why it arrives wrapped in ResponseValidationError at all. The new is_domain_value_rejection classifier in libs.attio (exported alongside is_uniqueness_conflict) re-parses that body and maps it to the same domain_invalid noop the formatter path uses, without the post-failure re-read: a rejected write landed nothing, so there is no race to disambiguate. Non-matching SDK errors keep the existing recheck/classify flow — uniqueness conflicts still raise AttioConflictError, anything else still bubbles. The caller’s dead branch is gone; the envelope translation already maps domain_invalid to unresolved. The old test pinned the dead branch by injecting a synthetic AttioValidationError the real callee never raises. It now runs the real set_company_domain_if_empty against a mocked client whose PATCH raises the captured error, exercising the PATCH → SDKError → domain_invalid → unresolved flow end to end, with a companion pin that non-matching SDK errors still surface as failed.
Last modified on October 2, 2026