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.
Connection::UNRECOVERABLE_ERRORSlistsauthentication expired, sohandleError()→recycleDeadConnection(false)marks the connection dead instead of rebuilding it: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: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 violationis 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/jwtProvideris 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
mainwhile resolving #192; not introduced by it.