azure-functions-python-worker icon indicating copy to clipboard operation
azure-functions-python-worker copied to clipboard

Improve exception management when Blob does not exist

Open goiri opened this issue 5 years ago • 9 comments

When a function is triggered through HTTP with a named parameter (e.g., userparam) and we have a blob binding with such parameter in the name and it does not exist, the runtime throws:

System.Private.CoreLib: Exception while executing function: Functions.test_missing_blob. System.Private.CoreLib: Result: Failure
Exception: AttributeError: 'NoneType' object has no attribute 'type'
Stack:   File "...\azure-functions-core-tools\bin\workers\python\3.7\WINDOWS\X64\azure_functions_worker\dispatcher.py", line 306, in _handle__invocation_request
    pytype=pb_type_info.pytype)
  File "...\azure_functions_worker\bindings\meta.py", line 63, in from_incoming_proto
    return binding.decode(datum, trigger_metadata=metadata)
  File "...\azure-functions-core-tools\bin\workers\python\3.7\windows\x64\azure\functions\blob.py", line 78, in decode
    data_type = data.type

The definition of the function can be something like:

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get"
      ]
    },
    {
        "name": "notebooksindexin",
        "type": "blob",
        "direction": "in",
        "path": "file-{userparam}.json",
        "connection":"AzureWebJobsStorage"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

The key here is to pass "userparam" such that "file-{userparam}.json" does not exist.

This eventually fails and returns a 500. However, I don't think this is the most graceful behavior. My proposal would be to:

  1. Instead of waiting all the way to the dispatcher to get an empty "data" field, identify this early and log it in a friendly way.
  2. Surface it to the client with a full HTTP response that would say somewhere (maybe the body) that the blob is missing.

I am running the azure functions runtime locally and executing queries but I think that's not relevant other than I should be able to test a possible solution fast.

goiri avatar May 05 '20 00:05 goiri

I think this should result in None being passed into the function and the code can deal with it accordingly. I believe that's the behavior for the other languages.

anthonychu avatar May 08 '20 20:05 anthonychu

As I mentioned at the end, I think the behavior overall is fine but there are two things that would help:

  1. Improve the error reporting in the "func start" to show something more informative than "Exception: AttributeError: 'NoneType' object has no attribute 'type'", I would expect something like "file-{userparam}.json for {userparam}=noneexisting does not exist".
  2. In the HTTP reply, include something in addition to status_code=500 body="Internal error". Ideally, the proposed message. Even a 404 would be better.

Pushing the None to the Python code might be a little weird, given that if I specify a non-existing blob directly it doesn't end up in that. I would totally support if both situations end up passing a None.

goiri avatar May 08 '20 23:05 goiri

Yes, I think both cases should pass None. We want to let user code decide what to do because an error isn’t always the desired result. Here’s an example of another language (C# script) where it receives null if the file doesn’t exist and the function handles it by creating the file with a blob output: https://github.com/anthonychu/slack-user-change-alerts/blob/master/src/SlackUserChangeAlerts.Function/CheckUserListChanges/run.csx#L13

anthonychu avatar May 09 '20 04:05 anthonychu

Thanks @anthonychu, that would work for us. I can check if what I received is None and return my own 404 with a detailed message.

goiri avatar May 09 '20 04:05 goiri

Waiting for deployment

Hazhzeng avatar Aug 18 '20 21:08 Hazhzeng

FYI for anyone down the line running into this issue, the error described (exception ending in "data_type = data.type") can be worked around in python by removing types from the parameters. ie: def main(req: func.HttpRequest, notebooksindexin: bytes) -> func.HttpResponse: can be changed to the below to workaround the issue. def main(req, notebooksindexin):

WilliamShimizu avatar May 08 '21 07:05 WilliamShimizu

Hi guys, I am debugging my azure function locally and I keep on getting the same error presented above, But the thing is my blob actually exists so I have no clue why i'm getting this error ! I have tried what @WilliamShimizu suggested And it kind of helps, since it changed my error to the following : the JSON object must be str, bytes or bytearray, not NoneType

I tracked this error down to see from where it originated, and appearently one of my variables (oldSession) is equal to None, but the thing is this parameter is specified in bindings as an input, and it's corresponding blob actually exists and is not empty, so i trully don't understand why oldSession is equal to None:

"type": "blob",
"name": "oldSession",
"path": "azure-webjobs-secrets/camerarequest/session.txt",
"connection": "AzureWebJobsStorage",
"direction": "in"

Note : A colleague of mine uses the same code (since we both pulled from github) and we use the same azure storage and all, but it works fine for him, which basically means that there is no problem with the code neither with the blob (session.txt) !! Do you guys have any suggestions ? I would appreciate any kind of help

MountassirEl avatar Feb 07 '22 21:02 MountassirEl

When will this land? I'd rather get a None blob and handle it in my Python code than the current behavior of seeing error logs with 'NoneType' object has no attribute 'type'.

reubano avatar May 24 '22 16:05 reubano

Just realized something, how will we know the name of the blob we attempted to fetch if all we get passed is None?

reubano avatar May 24 '22 16:05 reubano