Skip to content

fix(http-client-python): raise azure-core error types for customized errors covering standard status codes#10998

Closed
l0lawrence wants to merge 1 commit into
microsoft:mainfrom
l0lawrence:l0lawrence/python-custom-error-status-mapping
Closed

fix(http-client-python): raise azure-core error types for customized errors covering standard status codes#10998
l0lawrence wants to merge 1 commit into
microsoft:mainfrom
l0lawrence:l0lawrence/python-custom-error-status-mapping

Conversation

@l0lawrence

@l0lawrence l0lawrence commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

When a TypeSpec operation declares a customized error model that covers one of the standard azure-core status codes (401, 404, 409, 304), the Python emitter did not consistently raise the dedicated azure-core error type:

  • Ranged custom error (e.g. 4XX) covering a standard code → fell through to a generic HttpResponseError.
  • Default custom error model (covers all non-success codes) → likewise fell through to HttpResponseError for the standard codes.

Only a custom error targeting a standard code specifically raised the dedicated type, so behavior was inconsistent across the single / ranged / default cases.

Fix

Standard status codes now always map to their dedicated azure-core error type via the operation error_map, regardless of whether a customized error model (single, ranged, or default) also covers them:

  • 401 → ClientAuthenticationError
  • 404 → ResourceNotFoundError
  • 409 → ResourceExistsError
  • 304 → ResourceNotModifiedError

Because map_error(status_code=..., response=..., error_map=error_map) runs first in the generated handler, these codes raise their dedicated type there. The customized error body continues to be deserialized and attached (model=error) to the generic HttpResponseError that is raised for any other (non-standard) status codes covered by the customized error.

Behavior change note

For a custom error that targets a standard code specifically (e.g. a 404 model), the dedicated error is still raised, but the deserialized custom body is no longer attached to it — map_error raises before deserialization, so exc.model is None for those codes. The body remains available on HttpResponseError for non-standard codes. The response-status-code-range mock-api tests were updated to reflect this.

Example generated output (ranged custom error + default custom error)

if response.status_code not in [204]:
    map_error(status_code=response.status_code, response=response, error_map=error_map)
    error = None
    if 494 <= response.status_code <= 499:
        error = _failsafe_deserialize(_models.ErrorInRange, response)
    else:
        error = _failsafe_deserialize(_models.DefaultError, response)
    raise HttpResponseError(response=response, model=error)

with the shared error map:

error_map: MutableMapping = {
    401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError,
    304: ResourceNotModifiedError,
}

Validation

  • Regenerated all Azure + unbranded spector specs (181 specs, 0 failures).
  • response-status-code-range mock-api tests pass (sync + async, 4/4), exercising both an in-range code and the single 404 → ResourceNotFoundError.
  • Added a fix changelog entry for @typespec/http-client-python.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:python Issue for the Python client emitter: @typespec/http-client-python label Jun 16, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-python@10998

commit: 5a0ec04

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/http-client-python
Show changes

@typespec/http-client-python - fix ✏️

Always populate the operation error_map with the standard azure-core error types (401 → ClientAuthenticationError, 404 → ResourceNotFoundError, 409 → ResourceExistsError, 304 → ResourceNotModifiedError), even when a customized error model covers those status codes. Previously, a standard status code covered by a customized ranged or default error model fell back to a generic HttpResponseError; it now raises its dedicated error type via map_error. The customized error body continues to be deserialized and attached to the HttpResponseError raised for other (non-standard) status codes.

@azure-sdk-automation

azure-sdk-automation Bot commented Jun 16, 2026

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

@iscai-msft iscai-msft 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 think this is too complicated, we can just make sure we still generate the error map like here. map_error should handle throwing the correct error bc it takes in the error map that we pass it, so we don't need to specifically raise ResourceNotFoundError etc later in the code. Then keep the part that make sure the deserialization model correctly maps to the status code, like in the generated code here.

…errors covering standard status codes

Always populate the operation error_map with the standard azure-core error types (401 -> ClientAuthenticationError, 404 -> ResourceNotFoundError, 409 -> ResourceExistsError, 304 -> ResourceNotModifiedError), even when a customized error model covers those status codes via a ranged or default error response. map_error then raises the dedicated error type instead of falling back to a generic HttpResponseError. The customized error body continues to be deserialized and attached to the HttpResponseError raised for other (non-standard) status codes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@l0lawrence
l0lawrence force-pushed the l0lawrence/python-custom-error-status-mapping branch from 57cf0b7 to 5a0ec04 Compare June 16, 2026 22:07
@l0lawrence

Copy link
Copy Markdown
Member Author

I think this is too complicated, we can just make sure we still generate the error map like here. map_error should handle throwing the correct error bc it takes in the error map that we pass it, so we don't need to specifically raise ResourceNotFoundError etc later in the code. Then keep the part that make sure the deserialization model correctly maps to the status code, like in the generated code here.

If error_map has the error then doesn't map_error raise before we deserialize the body

@microsoft-github-policy-service microsoft-github-policy-service Bot added the stale Mark a PR that hasn't been recently updated and will be closed. label Jul 2, 2026
@l0lawrence

Copy link
Copy Markdown
Member Author

Superseded by #11250, which uses an upstream branch instead of a fork branch.

@l0lawrence l0lawrence closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:python Issue for the Python client emitter: @typespec/http-client-python stale Mark a PR that hasn't been recently updated and will be closed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants