Skip to content

fix: split DB errors from ErrNotFound in OIDC link handlers#191

Merged
veverkap merged 3 commits into
mainfrom
copilot/fix-handle-link-callback-errors
May 3, 2026
Merged

fix: split DB errors from ErrNotFound in OIDC link handlers#191
veverkap merged 3 commits into
mainfrom
copilot/fix-handle-link-callback-errors

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 3, 2026

Three places in handler/oidc.go silently redirected users with user-facing "not found" messages on transient DB errors, masking failures with no log output.

Changes

  • handleLinkCallbackFindByID: branch on errors.Is(err, auth.ErrNotFound) — not-found → redirect "User not found"; other errors → slog.ErrorContext + redirect "Link verification failed"
  • handleLinkCallbackLinkOIDCSubject: add slog.ErrorContext before "Failed to link" redirect so store failures are observable
  • LinkFindByID: decompose err != nil || u.OIDCSubject != nil into three explicit branches — DB error → log + 500; ErrNotFound → 409; already-linked → 409
// Before
user, err := h.Users.FindByID(r.Context(), linkUserID)
if err != nil {
    http.Redirect(w, r, "/?oidc_link_error="+url.QueryEscape("User not found"), http.StatusFound)
    return
}

// After
user, err := h.Users.FindByID(r.Context(), linkUserID)
if err != nil {
    if errors.Is(err, auth.ErrNotFound) {
        http.Redirect(w, r, "/?oidc_link_error="+url.QueryEscape("User not found"), http.StatusFound)
    } else {
        slog.ErrorContext(r.Context(), "failed to look up user during OIDC link", slog.Any("error", err))
        http.Redirect(w, r, "/?oidc_link_error="+url.QueryEscape("Link verification failed"), http.StatusFound)
    }
    return
}

Greptile Summary

This PR correctly separates transient DB errors from ErrNotFound in three OIDC link handler code paths, adding slog.ErrorContext logging for previously silent failures. The logic and test additions are sound, with one minor semantic concern in the Link handler where a not-found user returns the same 409 "cannot link account" as an already-linked user, making the two conditions indistinguishable to API clients.

Confidence Score: 5/5

Safe to merge; all slog calls include context and error handling logic is correct — one P2 semantic nit on the 409 status for ErrNotFound

Only P2 findings present; the core error-splitting logic is correct, logging is properly instrumented with context, and new tests cover all new branches

handler/oidc.go lines 319–320 (409 vs 404 for ErrNotFound in Link handler)

Important Files Changed

Filename Overview
handler/oidc.go Correctly splits DB errors from ErrNotFound in handleLinkCallback and Link; minor semantic issue with 409 used for both "not found" and "already linked" in Link handler
handler/oidc_test.go Adds test coverage for the new DB-error branches in handleLinkCallback and Link; tests are well-structured and cover the intended code paths

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[handleLinkCallback / Link called] --> B[FindByID]
    B --> C{err?}
    C -- "errors.Is(ErrNotFound)" --> D[Redirect: User not found / 409 Conflict]
    C -- "other DB error" --> E[slog.ErrorContext\nRedirect: Link verification failed / 500]
    C -- nil --> F{OIDCSubject set?}
    F -- yes --> G[Redirect: Already linked / 409 Conflict]
    F -- no --> H[Continue OIDC flow]
    H --> I[LinkOIDCSubject]
    I --> J{err?}
    J -- yes --> K[slog.ErrorContext\nRedirect: Failed to link]
    J -- no --> L[Redirect: oidc_linked=true]
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
handler/oidc.go:319-320
**`ErrNotFound` returns same 409 as "already linked"**

When `FindByID` returns `ErrNotFound` in `Link`, the response (`409 "cannot link account"`) is now identical to the already-linked branch at line 327–329. A client that consumes this API has no way to distinguish "this user account no longer exists" from "this account already has an OIDC identity attached". Returning `http.StatusNotFound` (404) here would preserve the correct semantic and keep the two failure modes distinguishable.

```suggestion
		if errors.Is(err, auth.ErrNotFound) {
			writeError(r.Context(), w, http.StatusNotFound, "user not found")
```

Reviews (3): Last reviewed commit: "chore: merge origin/main and resolve con..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves observability and correctness in the OIDC account-linking flow by distinguishing “not found” from transient/unknown store errors, avoiding user-facing “not found” redirects that mask backend failures.

Changes:

  • In handleLinkCallback, branch on errors.Is(err, auth.ErrNotFound) for FindByID, and log + redirect a distinct message on non-ErrNotFound errors.
  • In handleLinkCallback, log failures from LinkOIDCSubject before redirecting.
  • In Link, split the combined err != nil || u.OIDCSubject != nil check into explicit branches, logging and returning 500 on non-ErrNotFound store errors.
Show a summary per file
File Description
handler/oidc.go Separates ErrNotFound from DB/store errors in OIDC linking paths and adds error logging for better operational visibility.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment thread handler/oidc.go
Comment thread handler/oidc.go
veverkap added 2 commits May 3, 2026 10:38
…k handlers

Cover the non-ErrNotFound FindByID path in handleLinkCallback (should redirect
with Link verification failed) and in Link (should return 500, not 409).
Kept our ErrNotFound→409 / other-errors→500 split in Link(); reverted
TestOIDCLink_userNotFound to expect 409 (StatusConflict) to match.
@veverkap veverkap merged commit ca2ef55 into main May 3, 2026
7 checks passed
@veverkap veverkap deleted the copilot/fix-handle-link-callback-errors branch May 3, 2026 14:58
github-actions Bot added a commit that referenced this pull request May 3, 2026
… split

- 'User not found' now correctly reflects only ErrNotFound from FindByID
- 'Link verification failed' now covers both FindByID and FindByOIDCSubject DB errors
- Warning block updated to mention both guard paths
- HTTP status table: Link 409 limited to ErrNotFound; Link 500 now includes FindByID DB errors

Reflects fix in #191.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
veverkap pushed a commit that referenced this pull request May 3, 2026
… split (#197)

- 'User not found' now correctly reflects only ErrNotFound from FindByID
- 'Link verification failed' now covers both FindByID and FindByOIDCSubject DB errors
- Warning block updated to mention both guard paths
- HTTP status table: Link 409 limited to ErrNotFound; Link 500 now includes FindByID DB errors

Reflects fix in #191.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants