Skip to content

Bot Framework Emulator on macOS Fails with POST 400 (Auth Error) on Local Python Bot Despite Exhaustive Troubleshooting #2236

@keeplearningprogramming

Description

I am developing a simple Python Echo Bot on a personal MacBook using the Bot Framework SDK and testing it locally with the Bot Framework Emulator. The bot runs perfectly when configured for an anonymous connection (no App ID/Password).

However, when I provide a valid MicrosoftAppId and MicrosoftAppPassword to both the bot's code and the Emulator, every single message attempt fails with a POST 400 error in the Emulator log. I have performed exhaustive troubleshooting that proves the credentials, code, and network connectivity to Azure are all correct. The failure appears to be happening specifically within the authentication token validation handshake between the Emulator and the Python SDK on macOS.
Environment

Operating System: macOS (Sonoma)

Machine: Personal MacBook Pro (no corporate VPN, firewall, or proxy)

Client: Bot Framework Emulator v4.x.x

Bot Runtime: Python 3.13

Bot Libraries: botbuilder-core, botbuilder-integration-aiohttp (tested with latest and 4.15.0)

What I Have Proven

The bot works 100% correctly when authentication is disabled on both the bot and the Emulator. This confirms the core application logic and local networking are fine.

Network connectivity to Microsoft's authentication servers is working. curl https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration returns a valid JSON response.

My system clock is correctly synchronized with time.apple.com.

Exhaustive Troubleshooting Steps Performed

I have systematically eliminated every standard cause for this error:

  1. Credential Integrity:

    Generated multiple new client secrets (passwords) in the Azure Portal to ensure I was using the "Value" and not the "Secret ID".

    Hard-coded the App ID and Password directly into the bot's configuration to eliminate any possibility of environment variables interfering.

    Verified the exact credentials being used by the Python adapter at runtime with print statements. The values printed in the terminal are an exact match to the values entered into the Emulator.

  2. Network and Environment:

    Changed the bot's host from localhost to the explicit IPv4 address 127.0.0.1 to prevent any IPv4/IPv6 name resolution issues.

    Verified the endpoint is http and not https for local testing.

    Confirmed I am on a personal network with no VPN or external firewalls active.

    Used lsof -i :3978 and kill -9 to ensure no other process was using the port.

  3. Software State and Versions:

    Performed a hard reset of the Bot Framework Emulator by manually deleting its configuration files from ~/Library/Application Support/BotFramework-Emulator/.

    Completely deleted and rebuilt the Python virtual environment (venv) to ensure all dependencies were cleanly reinstalled.

    Tested with the latest botbuilder-python SDK. When that failed, I uninstalled it and tested with a known stable version (4.15.0). The POST 400 error persists across all versions.

Minimal Reproducible Code

import sys
from aiohttp import web
from botbuilder.core import BotFrameworkAdapter, BotFrameworkAdapterSettings, TurnContext, ActivityHandler
from botbuilder.schema import Activity
from config import DefaultConfig

CONFIG = DefaultConfig()

Confirmed via print statements that these exact values are used

SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)
ADAPTER = BotFrameworkAdapter(SETTINGS)

class EchoBot(ActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
await turn_context.send_activity(f"You said: '{turn_context.activity.text}'")

BOT = EchoBot()

async def messages(req: web.Request) -> web.Response:
try:
activity = Activity().deserialize(await req.json())
auth_header = req.headers.get("Authorization", "")
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return web.json_response(response.body, status=response.status)
return web.Response(status=201)
except Exception as e:
print(f"Error processing activity: {e}", file=sys.stderr)
return web.Response(status=500)

APP = web.Application()
APP.router.add_post("/api/messages", messages)

if name == "main":
try:
web.run_app(APP, host="127.0.0.1", port=3978)
except Exception as error:
raise error

Core Question

Given that the connection works perfectly without authentication, and all credential and environmental issues have been exhaustively ruled out, what could cause the BotFrameworkAdapter to consistently fail its validation of the security token sent by the Emulator?

Is this a known, deep-level bug or incompatibility related to cryptographic libraries (PyJWT, etc.) specifically on macOS? Are there any lower-level debugging tools or logs within the Python SDK or Emulator that can expose the exact reason for the token validation failure?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions