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