Skip to content

# [@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) #731

Description

@ajasingh

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

  1. Build a Teams bot with @chat-adapter/teams.
  2. Post an adaptive card containing a Button (rendered as Action.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>
  3. Register a handler:
    bot.onAction(["thumbs_up", "thumbs_down"], async (event) => {
      console.log("clicked", event.actionId);
      await event.thread?.post("received");
    });
  4. In Teams, click the button once.

Expected Behavior

  • The button click is acknowledged; no error banner.
  • The onAction handler runs exactly once.

Actual Behavior

  • Teams shows "Unable to reach app. Please try again." on the card.
  • The onAction handler runs twice (Teams retries the invoke). Logs show
    the same actionId / 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
  };
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions