"203 Non-Authoritative Information" with an HTML page instead of JSON
Azure DevOps Services answers a REST call whose credentials it does not accept with a redirect to its sign-in page (HTTP 302), not with a 401. Clients that follow redirects by themselves, as PowerShell's Invoke-RestMethod, .NET's HttpClient, Python's requests and curl -L do, then fetch the sign-in page, which answers HTTP 203 Non-Authoritative Information with HTML. That is a 2xx status, so the code goes on and fails later, usually where it parses the HTML as JSON ("Unexpected character encountered while parsing value: <", "Expecting value: line 1 column 1"). The fix is almost always the token.
What we saw
A made-up token sent to a public organization on dev.azure.com, October 27, 2026:
- On a call that needs sign-in (the project list):
302 Foundtohttps://…vssps.visualstudio.com/_signin?realm=dev.azure.com&reply_to=…, the same answer as with no token at all. - Following the redirect:
203 Non-Authoritative Information,text/html, about 16 KB: the sign-in page. - The same call with the header
X-TFS-FedAuthRedirect: Suppress:401 Unauthorizedwith JSON,TF400813: The user 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' is not authorized to access this resource.The all-a user is anonymous: the token was not used.
The usual causes
- The token expired, was revoked, or became inactive. Azure DevOps emails you before a token expires. An expired or revoked token cannot be edited or regenerated: create a new one. A token found in a public GitHub repository is revoked automatically unless your tenant turned that off. Organization settings, Auditing lists
Token.PatExpiredEvent,Token.PatRevokeEventandToken.PatSystemRevokeEvent(kept 90 days). - The header. A personal access token goes in a Basic header: base64 of a colon followed by the token (the user name is empty),
Authorization: Basic BASE64(":" + token), orcurl --user ":$AZURE_DEVOPS_PAT". Microsoft documents only Basic for these tokens; a Bearer header is for Microsoft Entra tokens. - Another organization. A token is created for one organization. A token for "All accessible organizations" is a global token, and global tokens stop working on December 1, 2026.
- Scopes. Microsoft lists insufficient scopes among the reasons a token stops working: the call needs a scope the token was not given (Code (Read) for repositories and pull requests).
- Your sign-in. In an organization backed by Microsoft Entra ID, a token can stop working when its owner has not signed in to Azure DevOps for a while (90 days, or your tenant's sign-in frequency). Sign in on the web, complete the whole prompt, and try again.
From Microsoft's Use personal access tokens. On Azure DevOps Server, Microsoft adds that IIS Basic Authentication prevents token sign-in: keep it disabled.
Getting a 401 with a message instead
- Send
X-TFS-FedAuthRedirect: Suppress. Azure DevOps then answers 401 with a JSON message instead of the redirect, as above. The Azure SDK for Python added it to its Azure Pipelines credential in 2024 "to avoid redirect responses" (azure-sdk-for-python #37510), and Microsoft notes that Visual Studio may send it too. With it, an account that does not meet the tenant's sign-in policies gets 401 until it signs in again, according to the 2022 release notes. - Or stop the client from following redirects, and treat a 302 to
/_signinas "sign-in failed". - Either way, check the status and the
Content-Typebefore parsing: a 203, ortext/htmlfrom_apis, is the sign-in page, not an answer.
A public project hides a bad token
On a public project, Azure DevOps does not refuse a token it does not accept. The call runs as anonymous and returns what anyone can read: with our made-up token, the repository list of a public project came back 200, the same as with no token. A script with an expired token keeps reading, and fails only at the first call that needs sign-in, such as a write. To see whether the token was used, call https://dev.azure.com/{organization}/_apis/connectionData: with a token Azure DevOps accepts, authenticatedUser is you; without, its descriptor is System:PublicAccess;aaaaaaaa-….
In IntelliJ IDEA, Rider and the other JetBrains IDEs
Azure DevOps Pull Requests is a pull request plugin for JetBrains IDEs 2024.1 or later and Android Studio. It does not follow the redirect: a 302 to the sign-in page, a 203, or an HTML page where JSON should be is reported as a sign-in problem, for a token with the usual causes (expired, missing the Code scope, made for "All accessible organizations"), and the plugin offers to open Settings | Tools | Azure DevOps Pull Requests. On a public organization it checks whom Azure DevOps answered as: when a token comes back as the anonymous user, it says the token was not accepted instead of reading on as anonymous. In an organization that restricts personal access tokens, it can use your Azure CLI sign-in instead. Reading is free; voting, commenting and completing are Pro, with a 30-day trial.
Get it on JetBrains Marketplace
Azure DevOps is a trademark of Microsoft Corporation; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with or endorsed by Microsoft or JetBrains.
More: TF400813 · global personal access tokens retired · TF401444 · 日本語