restsharp/RestSharp · error · InvalidOperationException

Token endpoint returned an invalid response: {body}

Error message

Token endpoint returned an invalid response: {body}

What it means

Thrown by the OAuth2 endpoint authenticator when the token endpoint returned HTTP success (2xx) but the deserialized response body either is null or has an empty AccessToken. This means the body did not conform to the expected JSON token shape even though the status code was OK.

Source

Thrown at src/RestSharp/Authenticators/OAuth2/OAuth2EndpointAuthenticatorBase.cs:96

            var parameters = BuildRequestParameters();

            if (TokenRequest.ExtraParameters != null) {
                foreach (var kvp in TokenRequest.ExtraParameters)
                    parameters[kvp.Key] = kvp.Value;
            }

            using var content = new FormUrlEncodedContent(parameters);
            using var response = await _tokenClient.PostAsync(TokenRequest.TokenEndpointUrl, content, cancellationToken).ConfigureAwait(false);

            var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
                throw new HttpRequestException($"Token request failed with status {response.StatusCode}: {body}");

            var tokenResponse = JsonSerializer.Deserialize<OAuth2TokenResponse>(body);

            if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.AccessToken))
                throw new InvalidOperationException($"Token endpoint returned an invalid response: {body}");

            _accessToken = tokenResponse.AccessToken;
            _tokenExpiry = tokenResponse.ExpiresIn.HasValue
                ? DateTimeOffset.UtcNow.AddSeconds(tokenResponse.ExpiresIn.Value) - TokenRequest.ExpiryBuffer
                : DateTimeOffset.MaxValue;

            OnTokenResponse(tokenResponse);
            TokenRequest.OnTokenRefreshed?.Invoke(tokenResponse);

            return _accessToken;
        }
        finally {
            _lock.Release();
        }
    }

    public void Dispose() {
        if (_disposeClient) _tokenClient.Dispose();

View on GitHub (pinned to 6a50821692)

Solutions

  1. Capture the body included in the exception message and inspect its structure against OAuth2TokenResponse.
  2. Confirm TokenEndpointUrl points to the real token API route, not a browser-facing login page.
  3. If the provider uses different JSON casing, customize the JSON serialization or map fields accordingly.
  4. Verify the IdP returns the token in the standard 'access_token' field.

Example fix

// before
// token endpoint returns { "accessToken": "..." } (camelCase)

// after
// ensure the endpoint returns { "access_token": "..." } (snake_case)
// or configure a custom JSON deserializer matching the provider's contract
Defensive patterns

Strategy: try-catch

Try / catch

try { await authenticator.Authenticate(client, request, ct); } catch (InvalidOperationException ex) when (ex.Message.Contains("invalid response")) { /* capture body, verify endpoint shape/casing */ }

Prevention

When it happens

Trigger: Token endpoint returns 200 with a body that is not valid JSON for OAuth2TokenResponse, or JSON that lacks the access_token field, e.g. an HTML login page, an error envelope, or a different field naming (access_token nested under 'data').

Common situations: Token endpoint URL actually points to an HTML login/consent page rather than the API token route; the IdP uses a non-standard response shape (e.g. camelCase vs snake_case mismatch); a proxy/CDN injected an HTML error page with a 200 status; mismatch between System.Text.Json default casing and the provider's casing.

Related errors


AI-assisted analysis of restsharp/RestSharp@6a50821692 (2026-08-13). Data as JSON: /api/errors/21a9268999961193. Report an issue: GitHub.