Skip to main content

ADR-0010: Exception contract

  • Status: Accepted
  • Date: 2026-10-25

Context​

GitHub, GitLab, and Azure DevOps report failures with different HTTP status codes, SDK exceptions, and error bodies. Callers need one exception family so provider switching does not change error handling. ADR-0009 defines the error categories and pagination behavior; this ADR defines the concrete public exception types and the provider status mapping.

Decision​

Every provider failure derives from VcsProviderException.

ExceptionUsed whenContext properties
VcsProviderExceptionBase type for all provider failuresNone
VcsNotFoundExceptionA resource does not exist and no more specific type appliesResource
RepositoryNotFoundExceptionA repository does not existOwner, Project, Name
PullRequestNotFoundExceptionA pull request does not existRepository, Number
VcsAuthenticationExceptionCredentials are missing or invalidNone
VcsAuthorizationExceptionCredentials are valid but lack permissionNone
VcsRateLimitExceptionA provider rate limit is exceededRetryAfter (nullable)
VcsConflictExceptionMerge conflict, changed source commit, or invalid state transitionNone
VcsUnsupportedOperationExceptionThe provider or current project settings cannot perform the operationProvider, Operation

HTTP status mapping​

Provider statusPublic exceptionNotes
400VcsProviderExceptionMalformed or invalid request. v1 has no separate validation exception.
401VcsAuthenticationExceptionMissing, expired, or invalid token.
403VcsAuthorizationExceptionValid token without permission. If rate-limit headers are present, use VcsRateLimitException instead.
404VcsNotFoundExceptionUse RepositoryNotFoundException or PullRequestNotFoundException when the resource type is known.
405VcsUnsupportedOperationExceptionThe provider cannot perform the operation.
409VcsConflictExceptionMerge conflict, stale source commit, or invalid state transition.
422VcsConflictException or VcsProviderExceptionUse VcsConflictException for state conflicts; otherwise use VcsProviderException.
429VcsRateLimitExceptionSet RetryAfter from the provided delay or reset time, otherwise null.
5xx or network failureVcsProviderExceptionWrap the original exception. Treat the failure as transient.

Provider-specific signals map to the same types:

  • GitHub. Octokit AuthorizationException, NotFoundException, RateLimitExceededException, and ApiException are translated by the provider.
  • GitLab. REST response status codes and HttpRequestException are translated by the provider.
  • Azure DevOps. VssUnauthorizedException, VssResourceNotFoundException, and VssServiceException are translated by the provider.

Rules:

  • Public methods never throw raw HttpRequestException, Octokit exception types, or Azure DevOps SDK exception types. They become the inner exception of the mapped VcsManager exception.
  • Authentication and authorization stay separate because the caller action differs: supply a new token versus request more permission.
  • VcsRateLimitException.RetryAfter is null when the provider gives no delay or reset time.
  • VcsConflictException covers merge conflicts and merge attempts against a source commit that changed after the caller read it.
  • VcsUnsupportedOperationException covers merge methods that the provider cannot execute for the current project settings, and other unsupported operations.
  • Messages include the provider, operation, and safe resource identifiers. They never include tokens, authorization headers, or full request bodies.

Consequences​

  • Callers catch one base type for generic handling and specific types for targeted recovery.
  • Provider packages own the translation from native errors. The core project has no provider dependency.
  • Each provider test suite must cover status mapping and inner-exception preservation.
  • A new error category requires a new exception type and an update to this ADR.

References​