Skip to content

Reset state on credential loading failure#1107

Merged
mtmk merged 1 commit into
mainfrom
fix-connect-hang-missing-creds
Apr 15, 2026
Merged

Reset state on credential loading failure#1107
mtmk merged 1 commit into
mainfrom
fix-connect-hang-missing-creds

Conversation

@mtmk

@mtmk mtmk commented Apr 13, 2026

Copy link
Copy Markdown
Member

When ConnectAsync throws during credential loading (e.g. missing creds file), ConnectionState stays at Connecting and the waiter TCS is never completed. A second ConnectAsync call then hangs forever waiting on that TCS.

Wrap credential loading in InitialConnectAsync with a try-catch that resets ConnectionState to Closed and faults the waiter, matching the existing pattern for socket and setup failures. Also broaden the catch in LoadCredsFile from NatsException to Exception so IO errors like FileNotFoundException are wrapped properly.

fixes #1104

When ConnectAsync throws during credential loading (e.g. missing creds
file), ConnectionState stays at Connecting and the waiter TCS is never
completed. A second ConnectAsync call then hangs forever waiting on
that TCS.

Wrap credential loading in InitialConnectAsync with a try-catch that
resets ConnectionState to Closed and faults the waiter, matching the
existing pattern for socket and setup failures. Also broaden the catch
in LoadCredsFile from NatsException to Exception so IO errors like
FileNotFoundException are wrapped properly.
@synadia-claude-reviewer

synadia-claude-reviewer Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @mtmk's task in 2m 1s —— View job


PR Review: Reset state on credential loading failure

  • Read CLAUDE.md for repo guidelines
  • Examine PR diff
  • Read changed source files for context
  • Analyze correctness and thread safety
  • Post review findings

Summary

The fix is correct and well-targeted. The new try-catch in InitialConnectAsync (NatsConnection.cs:397-417) exactly mirrors the existing pattern already used for socket failures (line 437-448) and setup failures (line 451-470):

  • Resets ConnectionState to Closed inside _gate lock (allows callers to retry)
  • Faults _waitForOpenConnection TCS via TrySetObservedException (unblocks any concurrent ConnectAsync waiters)
  • Immediately replaces the TCS so future callers get a fresh one
  • Re-throws for the initiating caller

The LoadCredsFile broadening (NatsExceptionException) is the right call: new StreamReader(path) can throw FileNotFoundException, IOException, UnauthorizedAccessException, etc. Before this fix, those would escape the catch unwrapped and not be caught as NatsException in ConnectAsync's outer loop, leaving the connection state stuck at Connecting.

No double-wrapping: when LoadCredsFile throws NatsException, the ex as NatsException cast in InitialConnectAsync succeeds, so the original exception is used directly.

One minor consistency note

LoadCredsContent (line 149 of UserCredentials.cs, unchanged) still catches only NatsException, making the two loader methods asymmetric:

// LoadCredsContent – unchanged, still NatsException only
catch (NatsException e)

// LoadCredsFile – correctly broadened in this PR
catch (Exception e)

StringReader won't throw IO errors and ParseCreds only explicitly throws NatsException, so this isn't a real bug. But broadening LoadCredsContent to Exception would match the pattern for defensive completeness.

No issues found with:

  • Thread safety: all state mutations are inside _gate lock
  • TCS lifecycle: old TCS faulted before new one assigned — concurrent waiters get the exception
  • RetryOnInitialConnect interaction: credential errors surface as NatsException, caught by ConnectAsync's retry loop — consistent with other failure modes
  • Test: Task.WhenAny + 5 s timeout is the correct pattern for hang regression tests

Verdict: LGTM. The fix is minimal, correct, and consistent with the existing error-handling pattern.

@github-actions

Copy link
Copy Markdown

@mtmk

mtmk commented Apr 14, 2026

Copy link
Copy Markdown
Member Author

hey @Prochy are you able to confirm this fix is working for you?

@Prochy

Prochy commented Apr 15, 2026

Copy link
Copy Markdown

hey @Prochy are you able to confirm this fix is working for you?

Just tested, it works.

@mtmk
mtmk requested a review from scottf April 15, 2026 10:11

@scottf scottf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mtmk
mtmk merged commit 16d7eac into main Apr 15, 2026
26 checks passed
@mtmk
mtmk deleted the fix-connect-hang-missing-creds branch April 15, 2026 10:26
This was referenced Apr 16, 2026
@mtmk mtmk mentioned this pull request May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Second ConnectAsync call stuck if CredsFile doesn't exist during first call

3 participants