Skip to content

Actor Issuer Binding Already Exists

URI: https://docs.ankatech.co/errors/actor-issuer-binding-already-exists
HTTP Status: 409 Conflict

When you see this

The (issuer, subject) pair is already bound in this tenant. A presented token resolves on that pair, so a second binding for the same pair would make the actor it authenticates depend on which row was read first.

The rule is per tenant. The same pair under a different tenant is a successful create, and must stay one: a global constraint would let one tenant's binding block another's and answer it with this status, which is a cross-tenant existence oracle produced by a constraint that merely looks stricter.

Common Causes

  • Re-submitting a bind that already succeeded, for example after a client retry.
  • Binding the same external subject to a second actor without removing the first binding.
  • Two concurrent binds of the same triple: the read-then-write is not atomic, so the database uniqueness constraint stops the second and it is reported here rather than as a generic conflict.

Response Example

{
  "type": "https://docs.ankatech.co/errors/actor-issuer-binding-already-exists",
  "title": "Actor Issuer Binding Already Exists",
  "status": 409,
  "detail": "The subject '3Nk1QpZa8vXcR2LmY0wT@clients' from https://acme.eu.auth0.com is already bound here, to actor 5a83f0d6-91c7-4b2e-8f60-3d17ae924cb8. A presented token resolves on the (issuer, subject) pair, so a second binding for the same pair would make the actor it authenticates depend on which row was read first. Remove the binding on actor 5a83f0d6-91c7-4b2e-8f60-3d17ae924cb8 to point that subject at a different actor.",
  "instance": "/api/v3/admin/tenants/3f9a1b62-5d47-4c8e-b0a2-91d7e4f60cc5/workload-identity/actors/5a83f0d6-91c7-4b2e-8f60-3d17ae924cb8/issuer-bindings",
  "timestamp": 1730000000,
  "extensions": {
    "requestId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.

Two shapes of this refusal, and only one names the holder

The detail above is the common shape: the server found the existing row before writing, so it holds it and names the actor that owns it. You do not have to look it up.

There is a second shape, and it is deliberately actor-less:

The subject '3Nk1QpZa8vXcR2LmY0wT@clients' from https://acme.eu.auth0.com is already bound here. …
Remove the existing binding to point that subject at a different actor.

That one is the loser of a concurrent bind — two requests for the same (issuer, subject) pair raced, the pre-check missed, and the unique constraint refused the insert. At that point the transaction is already marked rollback-only and the persistence context is undefined after a constraint failure, so reading the holder would need a transaction the request no longer has. It reports no actor rather than guessing one: a guessed holder is a wrong diagnosis, not a missing one, and it would send you to remove a binding that does not exist.

Both shapes carry the same type, title and status. Only the detail differs.

How to Resolve

  1. Read the detail. In the common shape it names the actor that holds the binding — go straight to it. Only the concurrent-race shape omits it, and then you list the tenant's bindings for that issuer to find the existing row.
  2. If the subject should authenticate a different actor, remove the existing binding first, then bind it to the new actor.
  3. If this was a retry of a request that already succeeded, treat the outcome as success — the binding is in place.

For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.