Budibase/budibase · error · Error
Teams Bot API ${resp.status}: ${await resp.text()}
Error message
Teams Bot API ${resp.status}: ${await resp.text()} What it means
teamsPost sends an activity to the Azure Bot Framework REST API (POST /v3/conversations/{conversationId}/activities) using the bot-scoped OAuth token. Non-ok responses are thrown as this error with the status and response body. It is used both for replying in an existing conversation (replyToConversation) and for posting the adaptive escalation card after a conversation is created (sendMSTeamsNotification).
Source
Thrown at packages/server/src/escalation/notifications/ms-teams.ts:222
}
const teamsPost = async <T = void>(
serviceUrl: string,
token: string,
conversationId: string,
body: object
): Promise<T> => {
const url = `${serviceUrl.replace(/\/$/, "")}/v3/conversations/${encodeURIComponent(conversationId)}/activities`
const resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
if (!resp.ok) {
throw new Error(`Teams Bot API ${resp.status}: ${await resp.text()}`)
}
return resp.json()
}
// Replies to the requester in their originating conversation on escalation
// resume - DM as plain text, channel with an @mention so it's attributed.
export async function replyToConversation({
appId,
agentId,
channel,
text,
}: {
appId: string
agentId?: string
channel: ChatConversationChannel
text: string
}): Promise<void> {
if (!channel.conversationId) {View on GitHub (pinned to a81a902e9a)
Solutions
- Use the serviceUrl captured from the original conversation (channel.serviceUrl or the conversation-creation response) instead of the default region URL.
- On 401, force a token refresh (getOAuthToken caches with dynamic TTL — clear the OAUTH2_TOKEN cache entry for the bot scope) and retry.
- Verify the bot app is still installed in the target team/tenant and the user hasn't blocked it.
- Confirm the conversationId is still valid; if not, create a new conversation (DM path) before posting.
Example fix
// before await teamsPost(DEFAULT_SERVICE_URL, token, channel.conversationId, message) // 404: wrong region // after const serviceUrl = channel.serviceUrl || DEFAULT_SERVICE_URL await teamsPost(serviceUrl, token, channel.conversationId, message)
Defensive patterns
Strategy: try-catch
Validate before calling
if (!channel.conversationId) throw new Error("Cannot post: missing conversationId")
if (!channel.serviceUrl) console.warn("No stored serviceUrl; falling back to default region") Try / catch
try {
await teamsPost(serviceUrl, token, conversationId, message)
} catch (err) {
if (/Teams Bot API 40[134]/.test(err.message)) {
// 401: refresh token; 403/404: bot removed or conversation stale — recreate conversation
} else if (/Teams Bot API 5\d\d/.test(err.message)) {
// transient — retry with backoff
} else throw err
} Prevention
- Persist the serviceUrl from the original conversation and use it for replies
- Keep bot installation in the team/tenant active and the messaging endpoint deployed
- Refresh bot tokens on 401 instead of reusing cached values indefinitely
When it happens
Trigger: The Bot Framework POST returns non-2xx: 401 (bot token invalid/expired or wrong serviceUrl region), 403 (bot not installed in the team/user has blocked the bot), 404 (conversationId no longer exists or is from a different channel), or 5xx from Microsoft.
Common situations: channel.serviceUrl stale or region-specific while TEAMS_API_URL/DEFAULT_SERVICE_URL points at a different region; bot was uninstalled from the team after escalation was created; bot token cached past expiry; user blocked the bot so DMs are rejected; conversationId from an old escalation.
Related errors
- Teams Graph API ${resp.status}: ${await resp.text()}
- Teams create conversation failed (${createResp.status}): ${a
- Unexpected response when fetching openid-configuration: ${re
- unexpected response ${response.statusText}
- Unexpected response ${response.statusText}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/ccf24688b11c2e96.
Report an issue: GitHub.