From abbae58677291e8ca4e2eecffa431242a7681479 Mon Sep 17 00:00:00 2001 From: nobe4 Date: Fri, 17 Jul 2026 16:17:18 +0200 Subject: [PATCH 1/2] fix(github_org): handle 422 in a nicer way It's not possible to get from the API which user blocked you. This is great to prevent spam and abuses. It however makes it difficult to also detect early when an invite to an org is going to fail. At scale, this might mean that users see many invitation at once and decide to just block the user instead of refusing the invitations. In which case entitlements doesn't handle the resulting 422 and fails with ``` 'Octokit::Response::RaiseError#on_complete': PUT https://api.github.com/teams//memberships/: 422 - Validation Failed (Octokit::UnprocessableEntity) ``` This change hopes to ease this pain by warning but not failing on such cases. It would then be up to the owner of the orgs to verify which invitations went through. --- .../backend/github_org/service.rb | 3 +++ lib/version.rb | 2 +- .../backend/github_org/service_spec.rb | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/entitlements/backend/github_org/service.rb b/lib/entitlements/backend/github_org/service.rb index 65ce75f..17062e0 100644 --- a/lib/entitlements/backend/github_org/service.rb +++ b/lib/entitlements/backend/github_org/service.rb @@ -54,6 +54,9 @@ def add_user_to_organization(user, role) Entitlements.logger.warn "User #{user} not found in GitHub instance #{identifier}, ignoring." return false + rescue Octokit::UnprocessableEntity => e + Entitlements.logger.warn "Unprocessable entity when adding #{user} to organization #{org} with role #{role}: #{e.message}. This likely means the user blocked the invite, so we're ignoring them, but someone should check it." + return false end # Happy path diff --git a/lib/version.rb b/lib/version.rb index 412c637..06191dc 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -2,6 +2,6 @@ module Entitlements module Version - VERSION = "1.2.1" + VERSION = "1.2.2" end end diff --git a/spec/unit/entitlements/backend/github_org/service_spec.rb b/spec/unit/entitlements/backend/github_org/service_spec.rb index 6aebce7..05c8a99 100644 --- a/spec/unit/entitlements/backend/github_org/service_spec.rb +++ b/spec/unit/entitlements/backend/github_org/service_spec.rb @@ -201,6 +201,28 @@ expect(result).to eq(false) end end + + context "when the API returns an unprocessable entity error" do + it "warns and returns false" do + expect(logger).to receive(:debug).with("github.fake add_user_to_organization(user=bob, org=kittensinc, role=admin)") + expect(logger).to receive(:debug).with("Setting up GitHub API connection to https://github.fake/api/v3/") + expect(logger).to receive(:warn).with(/Unprocessable entity when adding bob to organization kittensinc with role admin.*likely means the user blocked the invite/) + + stub_request(:put, "https://github.fake/api/v3/orgs/kittensinc/memberships/bob").to_return( + status: 422, + headers: { + "Content-type" => "application/json" + }, + body: JSON.generate({ + "message" => "Unprocessable Entity", + "documentation_url" => "https://docs.github.com/rest" + }) + ) + + result = subject.send(:add_user_to_organization, "bob", "admin") + expect(result).to eq(false) + end + end end end From b67fc5044f78b7fbc9df4d59a3aa388371d7b2e9 Mon Sep 17 00:00:00 2001 From: nobe4 Date: Tue, 21 Jul 2026 09:13:02 +0200 Subject: [PATCH 2/2] fix(Gemfile): update the lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b6fbdff..b52155a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - entitlements-github-plugin (1.2.1) + entitlements-github-plugin (1.2.2) contracts (~> 0.17.0) faraday (~> 2.0) faraday-retry (~> 2.0)