Skip to content

AzureDevOpsProvider throws NotSupportedException instead of GitHostingException for an unrecognized pull request status #106

Description

@matt-edmondson

What's wrong

GitIntegration/Hosting/AzureDevOpsProvider.cs, ToGitPullRequestState(string? status):

private static GitPullRequestState ToGitPullRequestState(string? status) => status switch
{
    "active" => GitPullRequestState.Open,
    "completed" => GitPullRequestState.Merged,
    "abandoned" => GitPullRequestState.Closed,
    _ => throw new NotSupportedException($"Azure DevOps reported an unrecognised pull request status '{status}'."),
};

status comes from a raw JSON string field on a live external API (AzureDevOpsPullRequest.Status), not a closed local enum. Any pull request whose status is something other than the three known values — a future Azure DevOps status, a casing/locale variant, a service preview value, or null/empty on a malformed record — makes GetPullRequestsAsync throw an unhandled NotSupportedException.

This violates the invariant this repo's own design (and CLAUDE.md) states: a field a host reports must not escape as anything but a GitHostingException, specifically so the two providers (GitHubProvider/AzureDevOpsProvider) stay interchangeable behind a catch (GitHostingException). GitHubProvider.ToGitPullRequestState has an identically-shaped default arm, but there it's genuinely unreachable because Octokit's ItemState is a closed two-value enum mirroring GitHub's own closed open/closed field — Azure DevOps's status is an open string from the wire, so the same code shape is not equally safe there.

This is distinct from already-open issue #104 (which covers Number, Title, SourceBranch/TargetBranch, Author, WebURI bypassing ToHostValue) — #104 doesn't mention Status, and the fix belongs in the same method family, so it's worth fixing alongside #104 if convenient.

Why it matters

A caller who wrote the documented catch (GitHostingException) around both providers, expecting them to be interchangeable, will have this one specific exception type escape that catch — an availability bug on a code path meant to be exception-safe.

Suggested fix

Route the fallback through the GitHostingException hierarchy (e.g. throw GitHostingRequestException carrying the raw status and response body) instead of NotSupportedException, or add an Unknown state to GitPullRequestState the way GitChangeKind.Unknown and GitRefUpdateKind.Unknown already handle open-ended git output elsewhere in this codebase.

Acceptance criteria

  • An Azure DevOps pull request with an unrecognized status value surfaces as a GitHostingException (or maps to an Unknown state), not an unhandled NotSupportedException.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions