Unhandled host error occurs after function execution

71 Views Asked by At

I've written an Azure function that downloads a blob on an HTTP trigger. When I debug this function locally, code execution makes it through the entire function, past the return, and then throws a vague error before actually sending the return.

Here is the function:

public class GetBlob
    {
        private readonly IUpgradeDataRepositoryFactory _upgradeDataRepository;
        private readonly IAuthorizationHelper _authorizationHelper;

        public GetBlob(IUpgradeDataRepositoryFactory upgradeDataRepository, IAuthorizationHelper authorizationHelper){
            _upgradeDataRepository = upgradeDataRepository;
            _authorizationHelper = authorizationHelper;
        }

        [FunctionName("GetBlob")]
        public HttpResponseMessage Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "GetBlob/{id}")] HttpRequest req,
            string id, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            var clientIp = (req.Headers["X-Forwarded-For"].FirstOrDefault() ?? "").Split(new char[] { ':' }).FirstOrDefault();

            try{
                _authorizationHelper.AssertRequestIsAuthorized(req);

                var blobInfo = _upgradeDataRepository.UpdateDataItemRepository.Query(q => q.BlobName == id).FirstOrDefault();
                if(blobInfo != null){
                    using (var blobStream = GetBlobStream(id))
                    {
                        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
                        result.Content = new StreamContent(blobStream);
                        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                        result.Content.Headers.ContentDisposition.FileName = blobInfo.BlobFriendlyName;
                        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                        log.LogInformation(string.Format("Returning Update Blob: {0} to ip: {1}", id, clientIp));
                        return result;
                    }
                }
                else
                {
                    log.LogInformation(string.Format("Could not find update item record for blob id: {0} Ip: {1}", id, clientIp));
                    return new HttpResponseMessage(HttpStatusCode.NotFound);
                }
            }
            catch (AuthorizationException){
                log.LogInformation("Authorization failed");
                var message = new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.Unauthorized
                };
                throw new HttpResponseException(message);
            }
            catch (Exception ex){
                log.LogError(string.Format("Error getting update blob: {0} for IP: {1}", id, clientIp));
                log.LogError(ex.ToString());
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
            finally{
                log.LogInformation(string.Format("Exiting Get Update Blob: {0} for ip: {1}", id, clientIp));
            }
        }

        private static CloudStorageAccount GetStorageAccount()
        {
            var connParameter= "StorageAccountConnectionString";
            var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
            return CloudStorageAccount.Parse(connectionString);       
        }

        private static string GetContainerName(){
            var containerParameter= "UpdateBlobContainerName";
            var containerName = System.Environment.GetEnvironmentVariable($"{containerParameter}");
            return containerName;
        }


        private static Stream GetBlobStream(string id)
        {
            CloudStorageAccount account = GetStorageAccount();
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference(GetContainerName());
            var blob = container.GetBlobReference(id);

            return blob.OpenReadAsync().Result;
        }
    }

(Note 1: This is using HttpResponseMessage because it's replacing an old API and needs to integrate with old code)

(Note 2: Though I haven't shared the code for AuthenticationHelper or UpgradeDataRepository, both of these objects have been utilized and tested in a separate, working function successfully. UpgradeDataRepository connects to an Azure SQL database and queries some information - this is happening successfully in both the working function and in this function. However, the working function does not access blob storage, so the error must be related to using blob storage.)

Here is the terminal output:

[2024-03-27T19:04:34.288Z] Executing 'GetBlob' (Reason='This function was programmatically called via the host APIs.', Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e)
[2024-03-27T19:04:34.300Z] C# HTTP trigger function processed a request.
[2024-03-27T19:04:41.904Z] Returning Update Blob: 729ee33f-e283-420a-b898-648718139e3a to ip: 
[2024-03-27T19:04:41.906Z] Exiting Get Update Blob: 729ee33f-e283-420a-b898-648718139e3a for ip:
[2024-03-27T19:04:41.907Z] Executed 'GetBlob' (Succeeded, Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e, Duration=7650ms)
[2024-03-27T19:04:41.915Z] An unhandled host error has occurred.
[2024-03-27T19:04:41.916Z] Microsoft.WindowsAzure.Storage: Object reference not set to an instance of an object.

Notice the message "Executed 'GetBlob' (Succeeded)" happens outside/after my function execution, followed by the error message. The return does not make it to the app I am testing from.

The error message is vague, and my debugger isn't catching the step where this error occurs, but it is definitely happening after everything else shown executes. I even checked that the blob is downloaded successfully and attached to the response.

My only guess at this point is a version mismatch? Just in case, here is my project file as well:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.14">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.14" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.14">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

UPDATE: I tried replacing Microsoft.Windows.Azure with Azure.Storage.Blobs:

private static BlobServiceClient GetStorageAccount()
        {
            var connParameter= "StorageAccountConnectionString";
            var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
            return new BlobServiceClient(connectionString);           
        }

private static Stream GetBlobStream(string id)
        {
            BlobServiceClient account = GetStorageAccount();
            BlobContainerClient container = account.GetBlobContainerClient(GetContainerName());
            var blob = container.GetBlobClient(id);

            return blob.OpenReadAsync().Result;
        }

Now, I get a slightly different error in terminal, after everything appears to execute successfully:

[2024-03-28T12:09:20.144Z] Executed 'GetBlob' (Succeeded, Id=08567df1-e23f-4973-a9e5-a9750f0ff9ee, Duration=4304ms)
[2024-03-28T12:09:20.322Z] An unhandled host error has occurred.
[2024-03-28T12:09:20.323Z] System.Private.CoreLib: Value cannot be null. (Parameter 'buffer').
0

There are 0 best solutions below