Skip to content

Classify ECONNRESET as codes.Unavailable in Okta API error handling#157

Open
btipling wants to merge 1 commit into
mainfrom
bt/handle-econnreset-as-unavailable
Open

Classify ECONNRESET as codes.Unavailable in Okta API error handling#157
btipling wants to merge 1 commit into
mainfrom
bt/handle-econnreset-as-unavailable

Conversation

@btipling
Copy link
Copy Markdown
Contributor

@btipling btipling commented Apr 9, 2026

Summary

  • Adds syscall.ECONNRESET check in handleOktaResponseErrorWithNotFoundMessage to return codes.Unavailable
  • The Okta Go SDK makes HTTP calls directly (not through baton-sdk's uhttp), so proxy connection resets surfaced as codes.Unknown
  • IsSyncPreservable does not consider codes.Unknown preservable, so the c1z artifact was discarded on every retry, forcing syncs to restart from scratch
  • This matches the existing pattern in baton-sdk's uhttp BaseHttpClient (wrapper.go:472) which already classifies ECONNRESET as Unavailable

Test plan

  • CI passes
  • Verify that connection reset errors from proxy now result in codes.Unavailable instead of codes.Unknown

🤖 Generated with Claude Code

@btipling btipling requested a review from a team April 9, 2026 21:38
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Claude finished @btipling's task in 58s —— View job


PR Review: Classify ECONNRESET as codes.Unavailable

  • Gather context and read changed files
  • Review the diff against origin/main
  • Check existing error handling patterns
  • Post review feedback

Verdict: Looks good

This is a small, well-motivated change. The analysis:

Correctness

The change at pkg/connector/helpers.go:125-127 correctly adds an errors.Is(err, syscall.ECONNRESET) check early in handleOktaResponseErrorWithNotFoundMessage, returning codes.Unavailable. This directly mirrors the pattern in baton-sdk's uhttp/wrapper.go:472 and wrapper.go:501.

The placement is good — checking ECONNRESET before the url.Error / timeout / status code checks is the right order since ECONNRESET is a specific network-level error that should be caught before more general checks.

Motivation

The PR description explains the problem clearly: the Okta Go SDK bypasses baton-sdk's uhttp (which already handles ECONNRESET), so proxy connection resets surfaced as codes.Unknown. Since IsSyncPreservable doesn't consider codes.Unknown preservable, syncs were restarting from scratch on every retry. This fix closes that gap.

Minor note

The errors.Is check will correctly unwrap through *url.Error and other wrapper types, so even if the ECONNRESET is wrapped inside a url.Error, this check will still catch it. The existing url.Error timeout check below at line 129-133 would never fire for ECONNRESET errors since they'd be caught first — this is fine and expected since ECONNRESET is not a timeout.

One thing to note: if err is nil, errors.Is(err, syscall.ECONNRESET) returns false, so this is safe for the nil-error path (which falls through to convertNotFoundError returning nil).

No issues found. Ship it.


The Okta Go SDK makes its own HTTP calls (not through baton-sdk's
uhttp). When the proxy resets the TCP connection, the error surfaces
as syscall.ECONNRESET wrapped in a url.Error. Without explicit
handling, this flows through as codes.Unknown, causing IsSyncPreservable
to return false and the c1z artifact to be discarded on every retry.

This matches the existing pattern in baton-sdk's uhttp BaseHttpClient
(wrapper.go:472) which already classifies ECONNRESET as Unavailable
for connectors that use it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@btipling btipling force-pushed the bt/handle-econnreset-as-unavailable branch from e4523f1 to a06997d Compare April 9, 2026 21:40
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Claude finished @btipling's task in 55s —— View job


PR Review: Classify ECONNRESET as codes.Unavailable

  • Gather context and read changed files
  • Review the diff against origin/main
  • Check existing error handling patterns
  • Post review feedback

Verdict: Looks good

This is a small, well-motivated change. The analysis:

Correctness

The change at pkg/connector/helpers.go:132-134 correctly adds an errors.Is(err, syscall.ECONNRESET) check early in handleOktaResponseErrorWithNotFoundMessage, returning codes.Unavailable. This directly mirrors the pattern in baton-sdk's uhttp/wrapper.go which already classifies ECONNRESET as Unavailable.

The placement is good — checking ECONNRESET before the url.Error / timeout / status code checks is the right order since ECONNRESET is a specific network-level error that should be caught before more general checks.

Motivation

The PR description explains the problem clearly: the Okta Go SDK bypasses baton-sdk's uhttp (which already handles ECONNRESET), so proxy connection resets surfaced as codes.Unknown. Since IsSyncPreservable doesn't consider codes.Unknown preservable, syncs were restarting from scratch on every retry. This fix closes that gap.

Details

  • errors.Is will correctly unwrap through backoff.PermanentError*url.Error*net.OpError*os.SyscallErrorsyscall.ECONNRESET, as the comment explains. Good documentation.
  • If err is nil, errors.Is(err, syscall.ECONNRESET) returns false, so this is safe for the nil-error path (which falls through to convertNotFoundError returning nil).
  • The existing url.Error timeout check below at line 136-139 would never fire for ECONNRESET errors since they'd be caught first — this is fine and expected since ECONNRESET is not a timeout.
  • The function is called from 12 call sites across app.go, group.go, role.go, and user.go, so all Okta API paths benefit from this fix.

No issues found. Ship it.


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.

1 participant