Skip to main content

Troubleshooting: pending-compliance and erasure gates

A GDPR Article-17 erasure request owns the contact for the life of the request: from the moment you file it until the scheduled hard-delete runs (or you cancel), a small class of pre-write checks refuse operations against that contact. The refusal is deliberate — your own erasure request is holding the contact, not a platform verdict you need to appeal. Two error codes share that shape:
  • CONTACT_ERASURE_PENDING — an outbound send to the contact is refused while an erasure request for them is pending or executing.
  • ERASURE_COOLING_OFF_ACTIVE — a new erasure filing is refused because a pending or executing request already exists for the contact.
Both resolve one of two ways: wait for the scheduled hard-delete, or cancel the active request while it is still cancellable, then retry.

Cause table

The cooling-off window is per-tenant configurable and defaults to 7 days. While a request is pending inside it:
  • erasure-request filings, message sends, and other contact writes to that contact are refused,
  • the request stays cancellable — once the window lapses, the scheduler hard-deletes the contact and writes a per-resource audit chain, and the request is no longer cancellable.
Read a contact’s active request and its window with GET /contacts/:id/gdpr/erasure-requests — each row carries status (pending, executing, cancelled, completed) and cooling_off_ends_at.

CONTACT_ERASURE_PENDING (422)

The send pipeline checks the recipient before dispatch. A contact with a pending or executing erasure request fails that check, and the send never reaches the provider — for a campaign the recipient row is marked skipped with reason: "erasure_pending"; for a direct API send the error arrives in the standard envelope:
Fix.
  1. Cancel the erasure if the send is required — while the request is still pending, call POST /contacts/:id/gdpr/erasure-request/:requestId/cancel (an optional reason is recorded on the audit trail). Sends to the contact unblock immediately.
  2. Or wait for the window. When cooling_off_ends_at lapses, the scheduler hard-deletes the contact and writes the per-resource audit chain. After that, a send to the same address behaves as a send to an unregistered recipient.
  3. Do not retry-loop the 422. The refusal is deterministic for the life of the request — it clears only on a cancellation or on the hard-delete.
Retry-safe: yes, once resolved — the gate rejects before any send is dispatched, so nothing partial exists on the platform side to clean up. Retry the identical send after a cancel, or after the contact has been deleted.

ERASURE_COOLING_OFF_ACTIVE (409)

POST /contacts/:id/gdpr/erasure-request enforces one active erasure request per contact. A second filing lands on that uniqueness rule before anything is written, and the endpoint returns the 409 — the existing request is either still cancellable (pending) or already executing. Fix.
  1. List the contact’s requests to find the active one: GET /contacts/:id/gdpr/erasure-requests — the active row is the one with status pending or executing.
  2. If the active request is pending, cancel it with POST /contacts/:id/gdpr/erasure-request/:requestId/cancel, then re-file your new request — a corrected filing always goes through the cancel.
  3. If the active request is executing (or has completed), cancel is no longer available; the scheduled hard-delete is imminent or done — treat the conflict as already resolved and file the new request only if data for the contact still exists.
Retry-safe: an immediate retry returns the same 409 until the active request resolves; after a cancel or after the hard-delete, the same POST is accepted. No row was written on the 409, so there is nothing to roll back.

DSAR_NOT_CANCELLABLE is a different, terminal gate

DSAR_NOT_CANCELLABLE (409) is the terminal state of the cancel path — it means the request cannot be withdrawn because it does not exist or has already completed, failed, expired, or been cancelled. It is not an erasure-ownership gate like the two codes above; read the request status first with GET /compliance/dsar/:id and treat the 409 as the answer, not a retryable error. Its full recovery matrix sits on Troubleshooting: DSAR export, decrypt, and cancel failures.

Recovery summary

Every control on this page is a tenant-owned path: Orbit runs the erasure lifecycle and the pre-write gates; whether you cancel a request or wait for the cooling-off window — and how you fulfil the data subject’s request — is your call. Nothing here is a claim of GDPR, CCPA, or other compliance.