Skip to content

Tolerate missing x-ms-blob-type response header#7182

Merged
Jinming-Hu merged 1 commit into
Azure:mainfrom
clee704:blob-type-optional-header
Jun 20, 2026
Merged

Tolerate missing x-ms-blob-type response header#7182
Jinming-Hu merged 1 commit into
Azure:mainfrom
clee704:blob-type-optional-header

Conversation

@clee704

@clee704 clee704 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #7180.

Description

BlobClient::GetProperties() and BlobClient::Download() throw std::out_of_range when a successful (2xx) response omits the x-ms-blob-type header, because it is generated as a required header (an unguarded GetHeaders().at("x-ms-blob-type")). The SDK then crashes — with a non-StorageException — on an otherwise-valid 200, e.g. from the OneLake / ADLS Gen2 *.dfs. endpoint serving the Blob API, which returns 200 without that header.

This marks x-ms-blob-type optional on the Blob_GetProperties and Blob_Download responses so the generator guards it with the existing count(...) != 0 idiom — the same treatment ETag / Last-Modified already receive on those responses — and applies the matching guard in the generated rest_client.cpp. When the header is absent, BlobType defaults to an empty Models::BlobType and the read otherwise succeeds.

Mirrors the "striped blob" compatibility fix for x-ms-blob-sequence-number (PR #3932, commit 0e00a3a): same class of problem, same mechanism (an x-nullable directive plus the matching generated-file guard, committed together).

rest_client.cpp is AutoRest-generated and the C++ emitter is not publicly runnable, so the generated-file edit here is by hand to match the directive (as #3932 did). Please regenerate to confirm the directive reproduces this output.

Pull Request Checklist

  • C++ Guidelines
  • Doxygen docs — N/A (no public API surface change)
  • Unit tests — not added, mirroring Compatibility improvement for striped blob #3932 (which changed only these two files). A mock-transport test (per storage_retry_policy_test.cpp) asserting GetProperties/Download tolerate a 200 without x-ms-blob-type can be added if preferred.
  • No unwanted commits/changes
  • Descriptive title/description
    • PR is single purpose
    • Related issue listed
  • Comments in source — N/A (generated file)
  • No typos
  • Update changelog
  • Not work-in-progress
  • External references or docs updated — N/A
  • Self review of PR done
  • Any breaking changes? — No

Copilot AI review requested due to automatic review settings June 18, 2026 19:35
@github-actions github-actions Bot added Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Storage Storage Service (Queues, Blobs, Files) labels Jun 18, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your contribution @clee704! We will review the pull request and get back to you soon.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a crash in azure-storage-blobs when the service returns a successful response (200/206) that omits the x-ms-blob-type header. It makes the header optional in the Swagger customization and updates the generated protocol-layer response parsing to guard header access, preventing a std::out_of_range from std::map::at.

Changes:

  • Updated the Swagger AutoRest directives to mark x-ms-blob-type as x-nullable (and set a default) for Blob_GetProperties and Blob_Download responses.
  • Updated rest_client.cpp deserialization to read x-ms-blob-type only when present (consistent with existing optional-header handling).
  • Added a changelog entry describing the bug fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
sdk/storage/azure-storage-blobs/swagger/README.md Adds an AutoRest directive to mark x-ms-blob-type optional for the affected operations so generation guards header access.
sdk/storage/azure-storage-blobs/src/rest_client.cpp Guards x-ms-blob-type header reads in the Download and GetProperties response parsers to avoid std::out_of_range.
sdk/storage/azure-storage-blobs/CHANGELOG.md Documents the crash fix in the Unreleased “Bugs Fixed” section.

@Jinming-Hu Jinming-Hu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, but we cannot modify generated code like this. I'll look into the issue later today.

@Jinming-Hu

Copy link
Copy Markdown
Member

@clee704 Could you please update your PR with the patch below and leave this change out of the CHANGELOG? We don't officially support using this SDK with OneLake, and I don't want to send the wrong signal or create confusion by implying that we do.

diff --git a/sdk/storage/azure-storage-blobs/src/rest_client.cpp b/sdk/storage/azure-storage-blobs/src/rest_client.cpp
index 27a65870b..ab0a62b47 100644
--- a/sdk/storage/azure-storage-blobs/src/rest_client.cpp
+++ b/sdk/storage/azure-storage-blobs/src/rest_client.cpp
@@ -3814,7 +3814,10 @@ namespace Azure { namespace Storage { namespace Blobs {
         response.Details.SequenceNumber
             = std::stoll(pRawResponse->GetHeaders().at("x-ms-blob-sequence-number"));
       }
-      response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+      if (pRawResponse->GetHeaders().count("x-ms-blob-type") != 0)
+      {
+        response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+      }
       if (pRawResponse->GetHeaders().count("x-ms-copy-completion-time") != 0)
       {
         response.Details.CopyCompletedOn = DateTime::Parse(
@@ -4060,7 +4063,10 @@ namespace Azure { namespace Storage { namespace Blobs {
         response.ObjectReplicationDestinationPolicyId
             = pRawResponse->GetHeaders().at("x-ms-or-policy-id");
       }
-      response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+      if (pRawResponse->GetHeaders().count("x-ms-blob-type") != 0)
+      {
+        response.BlobType = Models::BlobType(pRawResponse->GetHeaders().at("x-ms-blob-type"));
+      }
       if (pRawResponse->GetHeaders().count("x-ms-copy-completion-time") != 0)
       {
         response.CopyCompletedOn = DateTime::Parse(
diff --git a/sdk/storage/azure-storage-blobs/swagger/README.md b/sdk/storage/azure-storage-blobs/swagger/README.md
index 9370cb051..4a55faaf9 100644
--- a/sdk/storage/azure-storage-blobs/swagger/README.md
+++ b/sdk/storage/azure-storage-blobs/swagger/README.md
@@ -1044,6 +1044,8 @@ directive:
         $[status_code].headers["x-ms-legal-hold"]["x-nullable"] = true;
         $[status_code].headers["x-ms-immutability-policy-until-date"]["x-ms-client-path"] = "Details.ImmutabilityPolicy.ExpiresOn";
         $[status_code].headers["x-ms-immutability-policy-mode"]["x-ms-client-path"] = "Details.ImmutabilityPolicy.PolicyMode";
+        $[status_code].headers["x-ms-blob-type"]["x-nullable"] = true;
+        $[status_code].headers["x-ms-blob-type"]["x-ms-client-default"] = "";
         delete $[status_code].headers["Accept-Ranges"];
         delete $[status_code].headers["Content-Length"];
         delete $[status_code].headers["Content-Range"];
@@ -1150,6 +1152,8 @@ directive:
       $["x-ms-legal-hold"]["x-ms-client-name"] = "HasLegalHold";
       $["x-ms-legal-hold"]["x-ms-client-default"] = false;
       $["x-ms-legal-hold"]["x-nullable"] = true;
+      $["x-ms-blob-type"]["x-nullable"] = true;
+      $["x-ms-blob-type"]["x-ms-client-default"] = "";
       delete $["Accept-Ranges"];
       delete $["x-ms-or"];

Get Blob (Blob_Download) and Get Blob Properties (Blob_GetProperties)
deserialize the x-ms-blob-type response header unconditionally
(GetHeaders().at("x-ms-blob-type")), which throws std::out_of_range
when a successful (2xx) response omits it. Mark the header x-nullable on
those two response directives so the generator guards the access, and
apply the matching guard in the generated rest_client.cpp.
@clee704 clee704 force-pushed the blob-type-optional-header branch from a32bd85 to ae11ed9 Compare June 20, 2026 05:57
@clee704

clee704 commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Jinming-Hu — done. I've updated the PR with your patch (folded the x-nullable / x-ms-client-default into the existing Blob_GetProperties and Blob_Download response directives) and dropped the CHANGELOG entry. The PR now touches only rest_client.cpp and swagger/README.md, matching your diff. Appreciate you taking a look at the underlying issue.

@Jinming-Hu Jinming-Hu merged commit e9f2fa3 into Azure:main Jun 20, 2026
84 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Storage Storage Service (Queues, Blobs, Files)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BlobClient::GetProperties()/Download() throw std::out_of_range when a 2xx response omits x-ms-blob-type

4 participants