Microsoft Teams Error Guide: Power Automate 'The workflow run failed' Posting an Adaptive Card — Fix the Post Action
Fix Power Automate Workflows failing to post an Adaptive Card to Teams: correct malformed card JSON, handle 429 throttling, and refresh expired connections.
- #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
A Power Automate (Workflows) flow that posts an Adaptive Card into a Teams channel or chat fails the run at the “Post card in a chat or channel” action. The run history shows the action status and the underlying Teams/Graph error returned to the connector.
The workflow run failed.
Action 'Post_card_in_a_chat_or_channel' failed:
{ "status": 400, "message": "Invalid Adaptive Card JSON: unexpected token at line 1.
BadRequest - card content could not be parsed." }
Throttled runs instead show:
Action 'Post_card_in_a_chat_or_channel' failed:
{ "status": 429, "message": "Rate limit is exceeded. Try again in 27 seconds." }
Symptoms
- The flow’s run history shows a red X on the Teams “Post card” action.
- The card posts sometimes and fails other times (throttling), or fails every time (bad JSON/connection).
- A “connection is not valid / needs reauthorization” banner appears on the flow.
- The card posts but renders empty (valid JSON, wrong
contentTypewrapper). - Bulk/looped posts fail partway with 429s.
Common Root Causes
- Malformed card JSON — the
attachments/card payload built viaCompose/expressions produces invalid JSON (unescaped quotes, a trailing comma, an unresolved expression). - Wrong payload shape — passing raw card body instead of the
{ "type": "AdaptiveCard", ... }content, or omitting the message attachment wrapper the action expects. - Throttling (429) — a loop posts many cards fast and exceeds the connector/Teams rate limits.
- Expired connection — the Teams connection’s OAuth token expired or the owning account lost access to the target team.
- Missing membership — the flow’s identity is not a member of the target channel/chat.
Diagnostic Workflow
Open the failed action’s raw inputs and validate the card JSON separately. Pipe the exact posted content through a validator:
# Paste the action's 'inputs' card content into card.json, then:
jq empty card.json && echo "valid JSON" || echo "INVALID JSON"
# Validate against the Adaptive Card schema
npx adaptivecards-cli validate --version 1.5 ./card.json
Confirm the shape the “Post card” action needs — the content is the card object itself, not wrapped in attachments when using the native Teams action, but wrapped when posting via an HTTP/Graph action:
POST /teams/{team-id}/channels/{channel-id}/messages
{
"body": { "contentType": "html", "content": "<attachment id=\"1\"></attachment>" },
"attachments": [{
"id": "1",
"contentType": "application/vnd.microsoft.card.adaptive",
"content": "{\"type\":\"AdaptiveCard\",\"version\":\"1.5\",\"body\":[...]}"
}]
}
Reproduce a throttle and read the retry hint:
curl -i -X POST "https://graph.microsoft.com/v1.0/teams/${TEAM}/channels/${CH}/messages" \
-H "Authorization: Bearer ${T}" -H "Content-Type: application/json" \
-d @message.json
# HTTP/1.1 429 Too Many Requests
# Retry-After: 27
Example Root Cause Analysis
A deployment pipeline posted a per-service Adaptive Card to a release channel via a Workflow triggered by an HTTP request. It worked in testing but failed in production whenever a release touched many services: the flow looped over services and posted a card per iteration, and after ~15 rapid posts every subsequent action returned 429 and failed the whole run.
Two fixes solved it. First, they replaced the per-service loop with a single digest card summarizing all services, cutting posts from 20+ to 1. Second, they wrapped the post action in a Workflows retry policy (exponential, honoring Retry-After) and moved the card-building into a Compose action validated against the schema so a malformed expression could never silently produce broken JSON. Runs went green and the channel got one clean card per release instead of a burst.
Prevention Best Practices
- Build card JSON in a dedicated
Composeaction and validate it against the Adaptive Card schema before the post action. - Batch: post one digest card instead of looping many individual posts to avoid 429s.
- Configure the action’s retry policy to exponential and honor
Retry-Afteron 429/503. - Keep the Teams connection owned by a service account with stable membership in the target teams; monitor for expiry.
- Escape user-provided text going into the card to avoid producing invalid JSON.
- Add a run-failure alert (or a “configure run after” branch) so a failed post pages someone instead of vanishing.
Quick Command Reference
# Validate the exact card content the action posted
jq empty card.json && npx adaptivecards-cli validate --version 1.5 card.json
# Test the Graph channel-message post directly
curl -i -X POST "https://graph.microsoft.com/v1.0/teams/$TEAM/channels/$CH/messages" \
-H "Authorization: Bearer $T" -H "Content-Type: application/json" -d @message.json
# Read the throttle backoff hint
curl -sD - -o /dev/null ... | grep -i retry-after
Conclusion
A Workflows “Post card” failure is almost always one of three things: invalid card JSON, the wrong payload shape, or throttling. Validate the card content the action actually sent, confirm the attachment wrapper matches the action you’re using, batch posts into digests to stay under rate limits, and add retry-with-backoff plus a failure alert. With those in place, your flow posts clean cards reliably instead of failing mid-run.
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.