Microsoft Teams Error: 'Operation returned an invalid status code 'Forbidden'' — Cause, Fix, and Troubleshooting Guide
Fix the Bot Service SDK 'Operation returned an invalid status code Forbidden' (HTTP 403): app uninstalled, bot removed, or blocked by tenant policy.
- #microsoft-teams
- #troubleshooting
- #errors
- #bot-framework
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
The Azure Bot Service SDK throws ErrorResponseException with the message Operation returned an invalid status code 'Forbidden' when the Bot Framework connector returns HTTP 403 while posting an activity. Unlike a 401 (bad or missing token), a 403 means the connector accepted your identity but refused the action: the bot is no longer allowed to message that conversation.
The exception message from the .NET SDK:
Microsoft.Bot.Schema.ErrorResponseException:
Operation returned an invalid status code 'Forbidden'
The wrapped HTTP response:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": {
"code": "Forbidden",
"message": "..."
}
}
The failing call is an ordinary send against the conversation’s serviceUrl:
POST {serviceUrl}v3/conversations/{conversationId}/activities
A 403 here is a policy/state signal, not a transient fault. The bot has been removed from the team or chat, the app was uninstalled, the tenant blocked the bot, or an org policy disallows the bot messaging that user or scope. Retrying will not help.
Where it surfaces
- Sends throw
ErrorResponseException: Operation returned an invalid status code 'Forbidden'. - The wrapped
Response.StatusCodeis403. - A previously working proactive channel suddenly returns
403for every send. - Only some tenants return
403while others succeed with the same code path. - The failure persists across retries and backoff (it is not throttling).
- Users report they removed the app or the bot from the team/chat around the time sends began failing.
Identity and permission causes
- App uninstalled. The user or a team owner removed the app, so the bot has no permission to post to that conversation anymore.
- Bot removed from the team or chat. The bot was kicked from the specific team/channel/group chat even though the app may exist elsewhere.
- Tenant blocked the bot. A tenant admin blocked or disabled the app org-wide, or it was never permitted by app-setup/permission policies.
- Messaging disallowed by policy. Org-wide app settings, app permission policies, or messaging policies prevent the bot from messaging that user or scope.
- Wrong serviceUrl or tenant. Sending to a
serviceUrl/tenant the bot is not installed in produces a403because it has no standing there. - Stale proactive reference after re-provisioning. Reusing an old conversation reference after the app was reinstalled/reconfigured can hit policy that no longer permits the bot.
Tracing the failed authorization
Catch the exception and inspect the structured error body and status. In C#:
try
{
await turnContext.SendActivityAsync(activity, cancellationToken);
}
catch (ErrorResponseException ex) when (ex.Response?.StatusCode == HttpStatusCode.Forbidden)
{
logger.LogError("Connector 403. code={Code} message={Message} serviceUrl={ServiceUrl}",
ex.Body?.Error?.Code,
ex.Body?.Error?.Message,
turnContext.Activity.ServiceUrl);
// treat as stop-and-re-provision, not retry
}
Confirm whether the app is still installed for the target scope using Microsoft Graph (needs the appropriate application permission). For a chat:
curl -s -H "Authorization: Bearer $GRAPH_TOKEN" \
"https://graph.microsoft.com/v1.0/chats/$CHAT_ID/installedApps?\$expand=teamsApp" | jq '.value[].teamsApp.displayName'
For a team, the analogous path is /teams/{teamId}/installedApps. An empty result (or your app missing from it) confirms the bot is no longer installed. Also verify with a tenant admin whether app permission, app setup, or messaging policies block the app for the affected users.
Resolution
Treat 403 as terminal for that conversation: stop sending and re-provision rather than retrying. Remove or mark the stored conversation reference as invalid so a paced retry loop does not keep hammering a conversation the bot can no longer reach.
Confirm the app is still installed for the scope you are messaging (chat, channel, or user). If it was removed, the correct recovery is reinstallation by the user or admin — the bot cannot reinstall itself. Prompt the user to re-add the app, or have an admin redeploy it via the org app catalog.
Check tenant and policy configuration. Have an admin confirm the app is permitted by app permission policies, app setup policies, and any messaging policy, and that it is not blocked org-wide in the Teams admin center. For multi-tenant bots, verify you are sending with the correct serviceUrl and tenant for each conversation, and that the bot is actually installed in that tenant.
After re-provisioning, acquire a fresh conversation reference from a new incoming activity instead of resurrecting the old one.
Hardening access
- 403 is not transient. No amount of retry or backoff clears it; the bot’s standing must change.
- Distinguish from 401.
401is an auth/token problem;403is authorized-but-forbidden by state or policy. - The bot cannot reinstall itself. Recovery requires a user or admin action to re-add the app.
- Policies are tenant-scoped. The same bot can succeed in one tenant and be forbidden in another.
- Stop the retry loop. Persisting on a
403conversation wastes quota and can look like abusive traffic. - Verify serviceUrl/tenant pairing. Sending to the wrong cloud or tenant yields
403because the bot has no install there.
Related identity 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.