# [@chat-adapter/teams] Adaptive-card button clicks return an empty invoke response → Teams shows "Unable to reach app" and retries the invoke (duplicate action delivery)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 314
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 63
Description
Bug Description
When a user clicks a button (Action.Submit) on an adaptive card posted by a
Teams bot, the adapter's card.action invoke handler always replies with an
empty AdaptiveCardInvokeResponse (value: ""). Teams treats this as an
unsatisfactory/malformed invoke result, renders the generic "Unable to reach
app. Please try again." banner, and retries the invoke. The retry causes
the registered onAction handler to run a second time, so any side effect the
handler performs (post a message, create a ticket, resume a workflow, etc.)
happens twice for a single click.
Steps to Reproduce
- Build a Teams bot with
@chat-adapter/teams. - Post an adaptive card containing a
Button(rendered asAction.Submit):<Card> <Text>Was this helpful?</Text> <Actions> <Button id="thumbs_up" label="👍" value={JSON.stringify({ id: "x" })} /> <Button id="thumbs_down" label="👎" value={JSON.stringify({ id: "x" })} /> </Actions> </Card> - Register a handler:
bot.onAction(["thumbs_up", "thumbs_down"], async (event) => { console.log("clicked", event.actionId); await event.thread?.post("received"); }); - In Teams, click the button once.
Expected Behavior
- The button click is acknowledged; no error banner.
- The
onActionhandler runs exactly once.
Actual Behavior
- Teams shows "Unable to reach app. Please try again." on the card.
- The
onActionhandler runs twice (Teams retries the invoke). Logs show
the sameactionId/ activity processed two times, and any side effect is
duplicated.
Code Sample
Chat SDK Version
4.29.0
Node.js Version
No response
Platform Adapter
Microsoft Teams
Operating System
None
Additional Context
The card.action event handler returns a fixed invoke response whose value
is an empty string:
// packages/adapter-teams/src/index.ts (card.action handler)
this.app.on("card.action", async (ctx) => {
this.cacheUserContext(ctx.activity);
await this.handleAdaptiveCardAction(ctx);
return {
statusCode: 200,
type: "application/vnd.microsoft.activity.message",
value: "", // ← empty message value
};
});
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/adapter-teams/src/index.ts at the card.action handler and its Adaptive Card invoke response. Reproduce the single-button-click flow with the issue's onAction example, then verify that Teams receives an acknowledged response, shows no error banner, and invokes the handler only once.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100