Microsoft Teams Error: 'Service Unavailable' — Cause, Fix, and Troubleshooting Guide
Fix Microsoft Graph 503 Service Unavailable for Teams: honor Retry-After, exponential backoff with jitter, idempotent retries, and reading service health.
- #microsoft-teams
- #troubleshooting
- #errors
- #graph-api
Stuck on this Microsoft Teams error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
What this error means
An HTTP 503 from Microsoft Graph means the service is temporarily unable to handle the request — it is overloaded, or a backend dependency it relies on is momentarily unavailable. This is a transient, server-side condition, not a problem with your request. Graph typically returns the code serviceNotAvailable (and sometimes the generic UnknownError), and often includes a Retry-After header telling you how long to wait before trying again.
HTTP/1.1 503 Service Unavailable
Retry-After: 12
Content-Type: application/json
{
"error": {
"code": "serviceNotAvailable",
"message": "Service is temporarily unavailable. Please try again later.",
"innerError": {
"date": "2026-07-12T13:15:02",
"request-id": "e21b90aa-7744-4c1a-90bb-11ff22aa3355",
"client-request-id": "e21b90aa-7744-4c1a-90bb-11ff22aa3355"
}
}
}
A Graph 503 is distinct from a 502 you might see from the Bot Framework/Bot Service path. A 503 is Graph telling you the service itself is unavailable; treat it as retryable and back off. The right response is almost never to change the request — it is to wait and retry safely.
What users report
- Graph calls intermittently return HTTP 503 with code
serviceNotAvailableorUnknownError. - A
Retry-Afterheader is present on the 503 response. - Failures cluster in time — many requests fail together, then recover together.
- Retrying the exact same request a few seconds later succeeds unchanged.
- The Microsoft 365 service health dashboard shows a Teams or Graph advisory during the window.
- Long-running provisioning jobs fail partway through with no code or config change on your side.
Tenant and app configuration causes
- Transient service overload. Graph is shedding load and returning 503 until capacity recovers.
- Backend dependency down. A service Graph depends on is momentarily unavailable, surfaced to you as a 503.
- Ongoing service incident. An active Microsoft 365/Teams incident is degrading availability tenant-wide.
- Retry storm amplification. Aggressive, un-jittered client retries pile onto an already-stressed service and prolong the failures.
- No backoff on your side. The client treats 503 as a hard error or retries immediately, making the situation worse.
Confirming tenant configuration
Capture the status code, Retry-After, and the request identifiers on every failure — you need them for correlation and for any support case:
curl -s -D - -o /tmp/body.json -X GET \
"https://graph.microsoft.com/v1.0/teams/TEAM_ID/channels" \
-H "Authorization: Bearer $GRAPH_TOKEN" \
| awk 'BEGIN{IGNORECASE=1} /^HTTP|^Retry-After:|^request-id:|^client-request-id:|^x-ms-ags-diagnostic:/'
Pull the correlation IDs out of the body as well, since Graph echoes them in innerError:
jq '{code: .error.code, requestId: .error.innerError["request-id"],
clientRequestId: .error.innerError["client-request-id"]}' /tmp/body.json
Read the Retry-After value programmatically so your client waits the amount the service asked for:
RETRY=$(curl -s -D - -o /dev/null -X GET \
"https://graph.microsoft.com/v1.0/teams/TEAM_ID/channels" \
-H "Authorization: Bearer $GRAPH_TOKEN" \
| awk 'BEGIN{IGNORECASE=1} /^Retry-After:/{print $2}' | tr -d '\r')
echo "service asked us to wait: ${RETRY:-unset} seconds"
When you see clustered 503s, check the Microsoft 365 admin center service health dashboard for an active Teams/Graph incident before assuming the problem is yours.
Resolution
Honor Retry-After first: if the header is present, wait exactly that long before retrying. If it is absent, fall back to exponential backoff with jitter so concurrent clients do not retry in lockstep.
call_with_backoff() {
local url="$1" attempt=0 max=6
while :; do
code=$(curl -s -o /tmp/resp.json -D /tmp/hdr.txt -w '%{http_code}' \
-H "Authorization: Bearer $GRAPH_TOKEN" "$url")
if [ "$code" != "503" ]; then cat /tmp/resp.json; return 0; fi
attempt=$((attempt+1))
[ "$attempt" -gt "$max" ] && { echo "giving up after $max retries"; return 1; }
ra=$(awk 'BEGIN{IGNORECASE=1}/^Retry-After:/{print $2}' /tmp/hdr.txt | tr -d '\r')
if [ -n "$ra" ]; then
sleep "$ra"
else
base=$(( 2 ** attempt )); jitter=$(( RANDOM % (base + 1) ))
sleep $(( base + jitter ))
fi
done
}
call_with_backoff "https://graph.microsoft.com/v1.0/teams/TEAM_ID/channels"
Only retry idempotent operations blindly. For non-idempotent writes (creating a team, posting a message), pair the retry with a client-side dedupe key or a check-then-act so a retry after a 503 does not create a duplicate — the original request may have partially succeeded. Add a circuit breaker so that once you see sustained 503s you stop hammering Graph for a cool-down period and let the service recover, then resume.
Avoiding tenant drift
- Always honor Retry-After. When the header is present, wait exactly that long — do not retry sooner.
- Jitter your backoff. Un-jittered retries synchronize clients into a retry storm that prolongs the outage.
- Cap retries and add a circuit breaker. Bounded retries plus a cool-down stop your fleet from amplifying a service incident.
- Guard non-idempotent writes. Use dedupe keys or check-then-act so a post-503 retry cannot create duplicates.
- Log request-id and client-request-id. You cannot correlate with Microsoft support or your own traces without them.
- Check service health before deep debugging. A tenant-wide incident is not something your code can fix — confirm it first.
Related tenant errors
Fixed it? Get 500 Microsoft Teams & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.