git-ecosystem/git-credential-manager · error · ArgumentException
Provided URI ' ' is not a valid Azure DevOps hostname
Error message
Provided URI '{organizationUri}' is not a valid Azure DevOps hostname What it means
CreatePersonalAccessTokenAsync validates that the organization URI host is a recognized Azure DevOps host (dev.azure.com or *.visualstudio.com per UriHelpers.IsAzureDevOpsHost) before calling the API. A non-DevOps URL raises ArgumentException naming the offending URI.
Solutions
- Pass the hosted Azure DevOps organization URL, e.g. https://dev.azure.com/your-org/.
- For on-premises Azure DevOps Server, do not use this hosted-host validation path - configure the provider for Azure DevOps Server explicitly.
- Verify the host with UriHelpers.IsAzureDevOpsHost logic before calling; fix typos in the hostname.
Example fix
// before
var uri = new Uri("https://mycompany.visualstudio.com.default/MyProj");
// after
var uri = new Uri("https://dev.azure.com/myorganization"); Defensive patterns
Strategy: validation
Validate before calling
// Validate the org URI host before calling CreatePersonalAccessTokenAsync
var uri = new Uri(organizationUri);
bool isAdoHost = uri.Host == "dev.azure.com" || uri.Host.EndsWith(".visualstudio.com");
if (!isAdoHost) throw new ArgumentException($"{organizationUri} is not a hosted Azure DevOps organization URL"); Try / catch
try {
var pat = await restApi.CreatePersonalAccessTokenAsync(orgUri, accessToken);
} catch (ArgumentException ex) when (ex.Message.Contains("not a valid Azure DevOps hostname")) {
// normalize to https://dev.azure.com/{org} and retry
} Prevention
- Always construct the organization URL as https://dev.azure.com/{org}
- Do not pass repository or project URLs where an organization URI is expected
- Use a separate auth path for Azure DevOps Server (on-premises) instances
When it happens
Trigger: Calling AzureDevOpsRestApi.CreatePersonalAccessTokenAsync with a URI whose host is not an Azure DevOps hostname - e.g. an on-premises Azure DevOps Server URL, a GitHub/GitLab URL, or a mistyped host.
Common situations: Using GCM's Azure DevOps PAT creation against Azure DevOps Server (on-prem) which has a custom hostname; passing a full repository URL instead of the organization URL; DNS typos.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Only localhost is supported as a redirect URI.
- Must specify at least one OAuthAuthenticationModes
- Failed to create PAT
- Failed to create PAT
- Failed to find location service
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/9dfd939509fe129a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.AzureRepos/AzureDevOpsRestApi.cs:113
AzureDevOpsConstants.EnvironmentVariables.DevAadAuthorityBaseUri,
Constants.GitConfiguration.Credential.SectionName, AzureDevOpsConstants.GitConfiguration.Credential.DevAadAuthorityBaseUri,
out string redirectUriStr) &&
Uri.TryCreate(redirectUriStr, UriKind.Absolute, out Uri authorityBase))
{
return authorityBase;
}
return new Uri(AzureDevOpsConstants.AadAuthorityBaseUrl);
}
public async Task<string> CreatePersonalAccessTokenAsync(Uri organizationUri, string accessToken, IEnumerable<string> scopes)
{
const string sessionTokenUrl = "_apis/token/sessiontokens?api-version=1.0&tokentype=compact";
EnsureArgument.AbsoluteUri(organizationUri, nameof(organizationUri));
if (!UriHelpers.IsAzureDevOpsHost(organizationUri.Host))
{
throw new ArgumentException($"Provided URI '{organizationUri}' is not a valid Azure DevOps hostname", nameof(organizationUri));
}
EnsureArgument.NotNull(accessToken, nameof(accessToken));
_context.Trace.WriteLine("Getting Azure DevOps Identity Service endpoint...");
Uri identityServiceUri = await GetIdentityServiceUriAsync(organizationUri, accessToken);
_context.Trace.WriteLine($"Identity Service endpoint is '{identityServiceUri}'.");
Uri requestUri = new Uri(identityServiceUri, sessionTokenUrl);
_context.Trace.WriteLine($"HTTP: POST {requestUri}");
using (StringContent content = CreateAccessTokenRequestJson(organizationUri, scopes))
using (HttpRequestMessage request = CreateRequestMessage(HttpMethod.Post, requestUri, content, accessToken))
using (HttpResponseMessage response = await HttpClient.SendAsync(request))
{
_context.Trace.WriteLine($"HTTP: Response {(int)response.StatusCode} [{response.StatusCode}]");
string responseText = await response.Content.ReadAsStringAsync();
View on GitHub (pinned to e8ce762cd0)