microsoft/semantic-kernel · error · AgentInvokeException

Failed to post activity.

Error message

Failed to post activity.

What it means

An AgentInvokeException raised when posting the activity payload fails: POST {bot_endpoint}/conversations/{conversation_id}/activities returns a non-200 status. The posted activity is the user's message; if it isn't accepted the bot never receives it. Status is logged.

Source

Thrown at python/samples/demos/copilot_studio_agent/src/direct_line_agent.py:199

        # Step 2: Start a conversation if one hasn't already been started.
        if not self.conversation_id:
            start_conv_url = f"{self.bot_endpoint}/conversations"
            async with self.session.post(start_conv_url, headers=headers) as resp:
                if resp.status not in (200, 201):
                    logger.error("Failed to start conversation. Status: %s", resp.status)
                    raise AgentInvokeException("Failed to start conversation.")
                conv_data = await resp.json()
                self.conversation_id = conv_data.get("conversationId")
                if not self.conversation_id:
                    raise AgentInvokeException("Conversation ID not found in start response.")

        # Step 3: Post the message payload.
        activities_url = f"{self.bot_endpoint}/conversations/{self.conversation_id}/activities"
        async with self.session.post(activities_url, json=payload, headers=headers) as resp:
            if resp.status != 200:
                logger.error("Failed to post activity. Status: %s", resp.status)
                raise AgentInvokeException("Failed to post activity.")
            _ = await resp.json()  # Response from posting activity is ignored.

        # Step 4: Poll for new activities using watermark until DynamicPlanFinished event is found.
        finished = False
        collected_data = None
        watermark = None
        while not finished:
            url = activities_url if watermark is None else f"{activities_url}?watermark={watermark}"
            async with self.session.get(url, headers=headers) as resp:
                if resp.status == 200:
                    data = await resp.json()
                    watermark = data.get("watermark", watermark)
                    activities = data.get("activities", [])
                    if any(
                        activity.get("type") == "event" and activity.get("name") == "DynamicPlanFinished"
                        for activity in activities
                    ):
                        collected_data = data

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Inspect logged status: 400 => payload; 401 => token; 404 => conversation; 429 => slow down.
  2. Validate the payload from _build_payload matches the activity schema the bot expects.
  3. Refresh the token/conversation if expired; implement backoff for 429.

Example fix

// before
if resp.status != 200:
    raise AgentInvokeException("Failed to post activity.")

// after
if resp.status != 200:
    body = await resp.text()
    raise AgentInvokeException(f"Failed to post activity (status {resp.status}): {body}")
Defensive patterns

Strategy: retry

Try / catch

try:
    await post_activity(payload)
except AgentInvokeException as e:
    if "Failed to post activity" in str(e):
        logger.warning("Activity post failed; will retry once")
        await asyncio.sleep(1); await post_activity(payload)
    raise

Prevention

When it happens

Trigger: POST activities with the JSON payload returns 4xx/5xx — e.g. 400 bad payload, 401 expired token, 404 unknown conversation_id, 429 rate limited.

Common situations: Payload schema invalid for the bot; conversation_id expired/invalid; token expired; rate limiting from rapid message bursts.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/30cda30d393371c1. Report an issue: GitHub.