Skip to content

fix: recognize CloudAMQP's duplicate-invite 400 as already-invited - #19

Merged
luisina-santos merged 2 commits into
mainfrom
luisinasantos/fix-cloudamqp-duplicate-invite-400
Aug 10, 2026
Merged

fix: recognize CloudAMQP's duplicate-invite 400 as already-invited#19
luisina-santos merged 2 commits into
mainfrom
luisinasantos/fix-cloudamqp-duplicate-invite-400

Conversation

@luisina-santos

Copy link
Copy Markdown
Contributor

Summary

  • CreateAccount's retry path only treated HTTP 409 as already-exists, but CloudAMQP's POST /team/invite never returns 409 — a duplicate invite and a malformed email both come back as HTTP 400, distinguishable only by the JSON error body.
  • The client now reads that body on error responses and preserves the vendor message (e.g. "User is already invited", "Invalid email") instead of discarding it.
  • Added cloudamqp.IsAlreadyInvitedError, which matches HTTP 400 + a message containing "already invited", and wired it into CreateAccount's invite-error handling alongside the existing 409 check.

This lets a re-driven provisioning task (the C1 retry after the invitee hasn't yet accepted the first invite) land on the existing "invitation already pending" ActionRequiredResult path instead of hard-failing the task.

Fixes CXH-2233.

Note: the exact vendor message string ("User is already invited") is sourced from the ticket's live-API reproduction, not from CloudAMQP's published docs — if the wording ever changes on their end, this match will need to be revisited.

Test plan

  • go build ./...
  • go test ./... -count=1 (21 tests passing, including new pkg/cloudamqp/helpers_test.go cases for newAPIError and IsAlreadyInvitedError)
  • Manual repro against a live CloudAMQP tenant (not available in this session)

🤖 Generated with Claude Code

CreateAccount's retry path only treated HTTP 409 as already-exists, but
CloudAMQP's POST /team/invite never returns 409 — a duplicate invite and a
malformed email both come back as 400, distinguished only by the JSON error
body. The client now captures that body so IsAlreadyInvitedError can match on
"User is already invited" specifically, letting a re-driven provisioning
retry land on the existing ActionRequiredResult path instead of hard-failing.

Fixes CXH-2233.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 10, 2026

Copy link
Copy Markdown

CXH-2233

Comment thread pkg/cloudamqp/helpers.go Outdated
Comment thread pkg/connector/user.go
}
})
}
}

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.

🟡 Suggestion: The new unit tests cover the helpers well, but the mock in cmd/test-server/main.go still auto-accepts invites and returns 409 on duplicates (store.invite), so no end-to-end test exercises the 400 + {"error":"User is already invited"} shape that this PR exists to handle. Adding a pending-invite state to the mock that answers a re-invite with that 400 body would cover the CreateAccountActionRequiredResult path this fix relies on. (Confidence: high that the gap exists; non-blocking.)

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: fix: recognize CloudAMQP's duplicate-invite 400 as already-invited

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base dd0a28fcec7c.
Review mode: full
View review run

Review Summary

Scanned the full PR diff (pkg/cloudamqp/client.go, pkg/cloudamqp/helpers.go, new pkg/cloudamqp/helpers_test.go, pkg/connector/user.go) for security and correctness; no new issues found. Two of the three prior findings are addressed: the error-body read is now bounded by io.LimitReader with maxAPIErrorBodyBytes (pkg/cloudamqp/helpers.go:41), and the stale "after 409" wording is corrected (pkg/connector/user.go:255). Error classification stays code-based (IsNotFoundError, IsAlreadyExistsError), the invite error reaches IsAlreadyInvitedError unwrapped from InviteUser, defer rawResponse.Body.Close() still sits after the transport error check, and no dependency manifests changed.

The third prior finding is still open and is not re-flagged as new: cmd/test-server/main.go still auto-accepts invites and returns 409 on a duplicate, so no end-to-end path exercises the HTTP 400 already-invited error body this fix targets — the new coverage is unit-level in pkg/cloudamqp/helpers_test.go only. Since the PR notes the vendor message string comes from a live-API reproduction rather than published docs, that is the main residual risk.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

Note: the machine-readable review-state marker could not be written this run because the sandbox rejects the shell syntax required to emit it; the next review will run in full mode.

@github-actions github-actions Bot 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.

No blocking issues found.

Comment thread pkg/cloudamqp/helpers.go Outdated
- Give the //nolint:gosec in helpers_test.go an explanation (nolintlint
  was failing CI).
- Bound newAPIError's body read with io.LimitReader instead of an
  unbounded io.ReadAll.
- Extract the "already invited" match string into a const.
- Fix the now-stale "after 409" wording in CreateAccount's re-resolve
  error, since that branch is also reached via the new HTTP 400 path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@github-actions github-actions Bot 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.

No blocking issues found.

@luisina-santos
luisina-santos merged commit a3d336f into main Aug 10, 2026
11 checks passed
@luisina-santos
luisina-santos deleted the luisinasantos/fix-cloudamqp-duplicate-invite-400 branch August 10, 2026 20:35
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