Building, integrating, or customizing apps and workflows within Microsoft Teams using developer tools and APIs
Hi Anh Nguyễn Quang,
Thank you for posting your question in the Microsoft Q&A forum.
Based on my research, the behavior you observed is unexpected for server‑to‑server integrations. Under normal error conditions, these services return appropriate 4xx/5xx status codes with JSON error payloads rather than HTML. That said, there are some plausible cases that can explain what you are seeing:
- An HTTP client or intermediary follows a redirect (or reacts to
WWW-Authenticate) and lands on the interactive/authorizeor login page, which correctly returns200 + HTMLbut is not part of a server‑to‑server flow. - A reverse proxy/WAF/CDN or corporate gateway injects an HTML error page and, incorrectly, sets the status to 200, so your application receives HTML where JSON is expected.
- Certain edge scenarios around Graph (for example, batch requests) returning an HTML response. There has been a report about an issue with this situation here: https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/2661
To troubleshoot effectively, you can try classifying responses before parsing: only parse when Content-Type is application/json, and treat text/html (or bodies beginning with <!DOCTYPE html>/<html>) as an error, not data. Log correlation identifiers end‑to‑end by sending a client-request-id: <GUID> and capturing request-id, timestamps to make source identification and support escalation straightforward. Finally, check infrastructure so that proxies/gateways pass through graph.microsoft.com and login.microsoftonline.com without rewriting status codes or bodies, and, if you use batch, reproduce with single calls to determine whether the anomaly is batch‑specific.
My recommended approach this situation in practice is if the response’s Content-Type is JSON, parse it; if it is HTML, treat it as an error and differentiate login HTML (from login.microsoftonline.*, indicating a misrouted interactive path or unwanted redirect) from proxy HTML (indicating infrastructure rewriting). Then apply a disciplined retry policy: for the token endpoint, retry only transient errors (5xx, 429, temporarily_unavailable, server_error) with exponential backoff and jitter, and do not retry configuration/authentication errors (invalid_client, invalid_grant, unauthorized_client, invalid_scope) or any HTML response; for Graph, retry 429 or any HTML (respect Retry-After) and 5xx, and do not retry 400/401/403).
References:
- https://learn.microsoft.com/en-us/microsoft-cloud/dev/dev-proxy/concepts/how-to-debug-microsoft-graph-calls#generate-and-log-a-unique-client-request-id
- https://learn.microsoft.com/en-us/graph/errors
- https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/1501
I hope the information above helpful. If you have any further questions, please feel free to share.
If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.