Skip to content

Conversation

@sentry
Copy link
Contributor

@sentry sentry bot commented Jan 27, 2026

This PR addresses an issue where updating an organization's slug could result in a generic RpcRemoteException: Invalid service request if the desired slug was already reserved by another organization in the control silo.

Root Cause:
The original flow involved the region silo checking for slug availability against its local sentry_organization table. If no local conflict was found, it would proceed to call the control silo's update_organization_slug RPC. The control silo would then attempt to insert a TEMPORARY_RENAME_ALIAS into its global OrganizationSlugReservation table. If another organization had already reserved that slug, this INSERT would fail with an IntegrityError (unique constraint violation). This IntegrityError was caught by a generic exception handler in the RPC endpoint, which converted it into a ValidationError and returned a 400 status, leading to the unhelpful RpcRemoteException on the region side.

Solution:

  1. Control Silo (src/sentry/hybridcloud/services/control_organization_provisioning/impl.py): The update_organization_slug method now explicitly catches IntegrityError when attempting to save the OrganizationSlugReservation. If the error is due to a unique constraint violation on the slug, it raises a new, specific SlugCollisionRpcException.
  2. RPC Endpoint (src/sentry/api/endpoints/internal/rpc.py): The internal RPC endpoint now specifically catches SlugCollisionRpcException. Instead of converting it to a generic ValidationError (400), it returns an HTTP 409 Conflict status with a descriptive payload ({"detail": "slug_collision", "message": "Organization slug already in use"}).
  3. RPC Service Client (src/sentry/hybridcloud/rpc/service.py): The _raise_from_response_status_error method now checks for a 409 status code. If detected, it raises a new RpcSlugCollisionException (a subclass of RpcRemoteException) on the client side.
  4. Organization Provisioning Service (src/sentry/services/organization/provisioning.py): The _control_based_slug_change method now catches the RpcSlugCollisionException and re-raises it as the existing OrganizationSlugCollisionException.
  5. Organization Details Endpoint (src/sentry/core/endpoints/organization_details.py): This endpoint already has a try/except OrganizationSlugCollisionException block that returns a 409 Conflict with the user-friendly message "An organization with this slug already exists."

This change ensures that slug collision errors are properly identified and propagated through the hybrid cloud RPC system, providing a clear and actionable error message to the user.

Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 27, 2026
@rodolfoBee rodolfoBee marked this pull request as ready for review January 27, 2026 13:31
@rodolfoBee rodolfoBee requested review from a team as code owners January 27, 2026 13:31
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

# Slug collision - raise a specific exception that can be caught
raise RpcSlugCollisionException(
self.service_name, self.method_name, "Organization slug already in use"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

409 handling unreachable in test environment

High Severity

The new 409 status code handling that raises RpcSlugCollisionException is unreachable in test environments. The if in_test_environment(): block (lines 613-621) always raises a generic RpcRemoteException for any non-500 status code before reaching the 409 check. This means _control_based_slug_change in provisioning.py, which expects to catch RpcSlugCollisionException, will instead receive an uncaught generic exception in tests.

Fix in Cursor Fix in Web

)
except RpcSlugCollisionException:
raise OrganizationSlugCollisionException(
f"Organization slug '{slug}' is already in use"
Copy link
Contributor

Choose a reason for hiding this comment

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

Local call path misses slug collision exception

High Severity

In monolith or control silo mode, the local implementation is called directly without going through the RPC endpoint. When a slug collision occurs, SlugCollisionRpcException is raised by impl.py, but _control_based_slug_change catches RpcSlugCollisionException instead. These are unrelated exception classes with no inheritance relationship, so the exception propagates uncaught rather than being translated to OrganizationSlugCollisionException.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment on lines +627 to +629
raise RpcSlugCollisionException(
self.service_name, self.method_name, "Organization slug already in use"
)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we can generically assign all 409 response codes to slug collisions. I think we'd need a more general solution for getting exception messages out to handle this scenario though.

@markstory markstory self-assigned this Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

1 participant