Skip to content

An expired credential is unrecoverable, which defeats the dynamic token providers #197

Description

@levivannoort

Connection::UNRECOVERABLE_ERRORS lists authentication expired, so handleError() → recycleDeadConnection(false) marks the connection dead instead of rebuilding it:

// packages/nats/src/Connection.php:73
private const array UNRECOVERABLE_ERRORS = [
    'authorization violation',
    'authentication timeout',
    'authentication expired',
    ...
];

The rationale in reconnectsAfter() is sound for most of that list — retrying credentials the server just rejected turns a hard failure into a hot loop. But it does not hold for an expiry, because the client may well have a different credential available by the time it reconnects:

// packages/nats/src/Connection.php:~658 (buildConnectPayload)
// Dynamic providers are resolved on every (re)connect so refreshed
// tokens/JWTs take effect without rebuilding the connection.
if ($this->options->tokenProvider instanceof \Closure) { ... }
if ($this->options->jwtProvider instanceof \Closure) { ... }

NATS sends -ERR 'Authentication Expired' exactly when a live connection's credential lapses, which is the case those providers exist to handle. Today a worker with a rotating JWT goes dead at the first expiry and every later call raises, even though a reconnect would have picked up a valid token.

authorization violation is different and should stay unrecoverable: same credential, same rejection, every time.

Fix: make the expiry case conditional on a provider being configured — reconnect when tokenProvider/jwtProvider is set (there is a new credential to present), mark dead otherwise (there is not). Bounded reconnect attempts already cap the hot-loop risk if a provider keeps handing back a stale token.

Found by a review sweep over main while resolving #192; not introduced by it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions