Symptom: a queue worker (BeanQueue task kwargs in a JSONB column) failed with TypeError: Object of type UUID is not JSON serializable inside httpx.post(json=...), even though the enqueuer stored plain str(uuid.uuid4()). Fail-open error handling (catch + return False) made the drop invisible except in Sentry.
Root cause: the SQLAlchemy engine was created with json_deserializer = a wrapper over rapidjson.loads(..., uuid_mode=UM_CANONICAL). UM_CANONICAL doesn't just serialize UUIDs; on load it parses any canonical-form UUID string value back into a uuid.UUID object. So JSONB round trips are not type-stable: any string that happens to look like a canonical UUID comes back as a UUID object. Downstream code that re-serializes with stdlib json (httpx's json= kwarg) then raises. Prefixed ids (typeid usr_...) are unaffected, which makes the failure look field-specific and confusing.
Fix options: (a) coerce str() at the payload boundary where values re-enter stdlib-json serializers (targeted, what we shipped); (b) drop uuid_mode from the deserializer if nothing relies on it (engine-wide behavior change, riskier). Diagnosis one-liner: json_loads(json_dumps({'x': str(uuid.uuid4())})) and check the type.
Gotcha class: any 'smart' json_deserializer (datetime_mode, uuid_mode) on a DB engine breaks the assumption that JSON columns round-trip to JSON-native types, and the breakage surfaces far away, in whichever consumer re-serializes with a stricter encoder.