Skip to content

[BUG]: SearchIssuesRequest throws RepositoryFormatException for repository names that start with a hyphen #3082

Description

@asaf-clover

What happened?

Any search request that uses the Repos collection (SearchIssuesRequest, SearchCodeRequest, ...) throws RepositoryFormatException client-side, before any HTTP request is made, when the repository name part starts with a hyphen — even though such names are valid on GitHub and GitHub's Search API accepts the corresponding repo: qualifier.

Repro

var client = new GitHubClient(new ProductHeaderValue("repro"));

var request = new SearchIssuesRequest
{
    Repos = new RepositoryCollection { "myorg/-repo-with-leading-hyphen" }
};

// Throws Octokit.RepositoryFormatException before any HTTP call
await client.Search.SearchIssues(request);

Expected: the qualifier repo:myorg/-repo-with-leading-hyphen is sent to the search API.

Actual:

Octokit.RepositoryFormatException: The list of repositories must be formatted as 'owner/name' - these values don't match this rule: myorg/-repo-with-leading-hyphen

Root cause

The validation regex in Octokit/Helpers/StringExtensions.cs#L142:

static readonly Regex nameWithOwner = new Regex("[a-z0-9.-]{1,}/[a-z0-9.-_]{1,}", ...);

In the name part [a-z0-9.-_], the .-_ sequence is not three literal characters — it is a character range from . (0x2E) to _ (0x5F). The literal hyphen - (0x2D) sits just below the range start, so it is not in the class at all. A name whose first character is - therefore cannot match immediately after the /, and IsNameWithOwnerFormat() returns false. (Names with hyphens in the middle still pass only because the regex is unanchored, so the prefix before the first hyphen satisfies {1,}.)

This regressed in #1418 (the fix for #1406, repo names starting with an underscore): appending _ after the previously trailing literal - ([a-z0-9.-][a-z0-9.-_]) accidentally turned .- + _ into the .-_ range — fixing leading underscores while silently breaking leading hyphens. As a side effect the accidental range also admits characters that can never appear in a repository name (/, :, ;, <, =, >, ?, @, [, \, ], ^).

GitHub itself is fine with these names

  • Repository names starting with - can be created on github.com; we hit this in production against a real (private) organization repository whose name starts with -, returned by GitHub's own list organization repositories API.
  • The server-side search endpoint parses the qualifier without trouble: GET /search/issues?q=repo:owner/-nonexistent-repo+type:pr returns the standard 422 "The listed users and repositories cannot be searched..." validation response — exactly the same as for any nonexistent repo, not a query-parse error.

Suggested fix

Reorder the class so all three specials are literals — .-__.-:

static readonly Regex nameWithOwner = new Regex("[a-z0-9.-]{1,}/[a-z0-9_.-]{1,}", ...);

A - or . at the class edges and _ mid-class are all literal, so leading-hyphen (and leading-underscore, leading-dot) names validate, and the accidental junk range disappears. I'll open a PR with this change plus unit tests and link it here.

Versions

Octokit.net 14.0.0 (still present on current main); observed on .NET 8 / .NET 10 — platform-independent (pure regex).

Relevant log output

Octokit.RepositoryFormatException: The list of repositories must be formatted as 'owner/name' - these values don't match this rule: <org>/-<repo-name>
   at Octokit.SearchIssuesRequest.MergedQualifiers() in /_/Octokit/Models/Request/SearchIssuesRequest.cs:line 399
   at Octokit.BaseSearchRequest.get_Parameters() in /_/Octokit/Models/Request/BaseSearchRequest.cs:line 117
   at Octokit.SearchClient.SearchIssues(SearchIssuesRequest search) in /_/Octokit/Clients/SearchClient.cs:line 58

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    🆕 Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions