Lesstep
Azure DevOps Services · REST API

"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:

The usual causes

  1. 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.PatRevokeEvent and Token.PatSystemRevokeEvent (kept 90 days).
  2. 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), or curl --user ":$AZURE_DEVOPS_PAT". Microsoft documents only Basic for these tokens; a Bearer header is for Microsoft Entra tokens.
  3. 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.
  4. 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).
  5. 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

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 · 日本語