Search code examples
pythonbotframeworkchatbotskypeskype-bots

How do I solve a content type error in the Microsoft Bot Framework?


I'm building a bot with the Microsoft Bot Builder Python SDK. It is hosted on Heroku and exchanging text messages with Skype works well. But I've run into an issue with sending files. The bot generates a JSON file and sends it to the user. I'm using the _get_upload_attachment method of the attachments bot code (see with my changes below) and when the bot runs locally the file is sent to the Bot Framework Emulator with no issues. After deployment to Heroku the file is sent successfully via the test tool in the Bot Framework portal. But after triggering file sending from Skype, the error shown below occurs. Seems this error is specific for Skype channel.

What may provoke this error and how do I solve it?

Error:

2021-02-18T08:28:32.579790+00:00 app[web.1]: [on_turn_error] unhandled error: Operation returned an invalid status code 'Not Found'
2021-02-18T08:28:32.581176+00:00 app[web.1]: Traceback (most recent call last):
2021-02-18T08:28:32.581226+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/botbuilder/core/bot_adapter.py", line 128, in run_pipeline
2021-02-18T08:28:32.581227+00:00 app[web.1]: context, callback
2021-02-18T08:28:32.581229+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/botbuilder/core/middleware_set.py", line 69, in receive_activity_with_status
2021-02-18T08:28:32.581230+00:00 app[web.1]: return await self.receive_activity_internal(context, callback)
2021-02-18T08:28:32.581247+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/botbuilder/core/middleware_set.py", line 79, in receive_activity_internal
2021-02-18T08:28:32.581248+00:00 app[web.1]: return await callback(context)
2021-02-18T08:28:32.581249+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/botbuilder/core/activity_handler.py", line 68, in on_turn
2021-02-18T08:28:32.581250+00:00 app[web.1]: await self.on_message_activity(turn_context)
2021-02-18T08:28:32.581266+00:00 app[web.1]: File "/app/bot.py", line 140, in on_message_activity
2021-02-18T08:28:32.581267+00:00 app[web.1]: reply.attachments = [await self._get_upload_attachment(turn_context)]
2021-02-18T08:28:32.581268+00:00 app[web.1]: File "/app/bot.py", line 426, in _get_upload_attachment
2021-02-18T08:28:32.581269+00:00 app[web.1]: type = "application/json"
2021-02-18T08:28:32.581288+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/botframework/connector/aio/operations_async/_conversations_operations_async.py", line 1138, in upload_attachment
2021-02-18T08:28:32.581289+00:00 app[web.1]: raise models.ErrorResponseException(self._deserialize, response)
2021-02-18T08:28:32.581316+00:00 app[web.1]: botbuilder.schema._models_py3.ErrorResponseException: Operation returned an invalid status code 'Not Found

Get Attachment Method:

async def _get_upload_attachment(self, turn_context: TurnContext) -> Attachment:
    """
    Creates an "Attachment" to be sent from the bot to the user from an uploaded file.
    :param turn_context:
    :return: Attachment
    """
    with open("/tmp/exports/export.json", "rb") as in_file:
        json_data = in_file.read()
    connector = await turn_context.adapter.create_connector_client(
        turn_context.activity.service_url
    )
    conversation_id = turn_context.activity.conversation.id
    response = await connector.conversations.upload_attachment(
        conversation_id,
        AttachmentData(
            name = "export.json",
            original_base64 = json_data,
            type = "application/json"
        )
    )
    base_uri: str = connector.config.base_url
    attachment_uri = (
        base_uri
        + ("" if base_uri.endswith("/") else "/")
        + f"v3/attachments/{response.id}/views/original"
    )
    return Attachment(
        name = "export.json",
        content_type = "application/json",
        content_url = attachment_uri,
    )

UPD: I've replaced the JSON export by XLSX and facing the same issue: the XLSX file is sent successfully to the test tool in the Bot Framework portal (I can download and save it), but "The bot encountered an error or bug. To continue to run this bot, please fix the bot source code." message is returned to Skype channel. The Heroku log shows similar error as in the error sample above.

The file sending by the bot is triggered by the suggested actions button as shown on the screenshot below.

XLSX file sending by the bot


Solution

  • As you become more experienced with debugging Python code, you may find that a line number in a Python stack trace refers to the last line of a multi-line function call rather than the first. This makes sense when you consider how the interpreter reads one line at a time from top to bottom, and it can't call the function until it knows what all the arguments are. This means that even though the line you're seeing in the stack trace is type = "application/json", that's only because it's the last interpreted line of the function call so you should be paying attention to the function that's being called: upload_attachment. Your problem has nothing to do with the content type.

    We can see that upload_attachment is throwing a "Not Found" error. You may recognize that as the message associated with a 404. This means the underlying REST API operation that your connector client is calling does not exist for the given service URL, which means the operation isn't supported for the Skype channel. This shouldn't come as too much of a surprise, since most operations are channel-specific.

    I tried sending attachments to Skype the conventional way, and I got a 400 error:

    {
        "error": {
            "code": "BadSyntax",
            "message": "File attachments aren’t supported for this bot"
        }
    }
    

    This doesn't leave us with a lot of options. If you really want to send a file attachment to a user in Skype, you'll have to upload it to some external server yourself and then give its URL to the user. If you don't mind uploading the file to a public URL then that would be easiest, but if you want the file to be private then you'll need to upload it to some secure system that both the bot and the user have credentials for. That could get complicated.

    You may be disappointed with this answer, but I don't imagine you'll get a better one. Skype bots have been deprecated for over a year now, which means you shouldn't expect to get any official help with your Skype bot problems. My recommendation is to switch to another chat platform, like Teams.