Microsoft Teams Error Guide: 'Unable to reach app. Please try again.' Message Extension Timeout — Respond Within 15s
Fix the Microsoft Teams message extension 'Unable to reach app' timeout: return the invoke response within 15 seconds, cache searches, and offload slow work.
- #microsoft-teams
- #adaptive-cards
- #troubleshooting
- #errors
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.
Overview
When a Teams message extension (search or action command) takes too long to respond to an invoke activity, the Teams client gives up and shows a generic failure. The bot channel and your logs show the request timing out before your handler returns a response.
Unable to reach app. Please try again.
(bot log) composeExtension/query invoke did not return within 15s;
Teams closed the request. Upstream: context deadline exceeded (15000ms).
Symptoms
- A message extension search box shows “Unable to reach app. Please try again.” after a spinner.
- Action commands open the task module but the submit hangs, then errors.
- Intermittent failures that correlate with a slow downstream API or a cold-started function.
- The bot’s HTTP endpoint logs the
composeExtension/queryorcomposeExtension/submitActioninvoke arriving, but the response is sent after the client already disconnected. - Works locally with a fast backend, fails in production behind a slow dependency.
Common Root Causes
- Synchronous slow work — calling a slow API, database, or LLM inline before returning the invoke response, exceeding the ~15s invoke budget.
- Cold starts — the bot runs on a serverless plan that cold-starts beyond the timeout.
- No caching — every keystroke in a search command hits an expensive upstream with no debounce/cache.
- Blocking auth — performing an interactive sign-in or token exchange on the critical path instead of returning a
silentAuth/authresponse quickly. - Downstream throttling — the backing service returns 429s and your retry loop blows the budget.
Diagnostic Workflow
Confirm the invoke type and measure end-to-end handler latency. The incoming activity looks like:
{
"type": "invoke",
"name": "composeExtension/query",
"value": { "commandId": "searchIncidents",
"parameters": [{ "name": "query", "value": "payments" }] }
}
Return the response fast and shape it correctly (a search result list):
# Your bot must reply to the invoke with an HTTP 200 body like this,
# well under the 15s window:
cat <<'JSON'
{
"composeExtension": {
"type": "result",
"attachmentLayout": "list",
"attachments": [
{ "contentType": "application/vnd.microsoft.card.thumbnail",
"content": { "title": "INC-4821 Payments latency", "text": "SEV2 open" } }
]
}
}
JSON
Add timing around the handler and the downstream call:
[invoke] composeExtension/query received t=0ms
[query] upstream search start t=40ms
[query] upstream search returned t=16200ms <-- over budget
[invoke] response sent t=16240ms (client already gone)
For search commands, cache and debounce so repeated keystrokes don’t re-hit the upstream:
cache key = commandId + normalized(query) TTL = 60s
serve cached result instantly; refresh in background
Example Root Cause Analysis
An incident-lookup message extension let engineers search tickets from the Teams compose box. Each keystroke fired a composeExtension/query that called an internal search service, which itself fanned out to three systems. Under load the fan-out took 12-18 seconds, so roughly a third of searches showed “Unable to reach app.”
The handler was doing all the work synchronously on the invoke path. The fix was two-fold: add a 60-second in-memory cache keyed by the normalized query (most searches repeated the same few terms), and cap the upstream call with a hard 3-second deadline, returning a “still indexing, refine your search” result card if it wasn’t met instead of blocking. They also moved the bot from a cold-starting consumption plan to an always-warm instance. Timeouts dropped to near zero.
Prevention Best Practices
- Return the invoke response within the ~15s window, always; treat it as a hard budget.
- Put a strict deadline (a few seconds) on any downstream call and return a partial/“refine” result on timeout.
- Cache and debounce search-command queries; identical keystrokes should not re-hit the backend.
- Keep the bot warm (avoid cold starts) for latency-sensitive extensions.
- Offload heavy work: return a quick acknowledgment card, then update via proactive message when done.
- Handle downstream 429s with a bounded retry that respects
Retry-Afterand never exceeds the budget.
Quick Command Reference
# Tail bot logs for slow invokes
journalctl -u teams-bot --since '15 min ago' | grep composeExtension
# Time the downstream dependency directly
time curl -s "https://search.internal/api?q=payments" >/dev/null
# Confirm the bot messaging endpoint responds quickly
curl -w 'time_total=%{time_total}\n' -o /dev/null -s \
-X POST https://bot.example.com/api/messages -H 'Content-Type: application/json' -d '{}'
Conclusion
“Unable to reach app” on a message extension almost always means your handler blew the ~15-second invoke budget. The client disconnects even though your code eventually returns a valid response. Treat the invoke path as latency-critical: cap downstream calls with hard deadlines, cache and debounce searches, keep the bot warm, and offload heavy work to a proactive update. Respond fast and the extension stays reliable under load.
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.