Skip to content

[python] fix: omit content-type header when optional body is absent#11272

Draft
msyyc wants to merge 1 commit into
mainfrom
fix/optional-body-content-type-11253
Draft

[python] fix: omit content-type header when optional body is absent#11272
msyyc wants to merge 1 commit into
mainfrom
fix/optional-body-content-type-11253

Conversation

@msyyc

@msyyc msyyc commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

For #11253

Problem

For an operation with an optional body whose content-type is modeled as required/constant (a single specific media type, e.g. azure-batch's application/json; odata=minimalmetadata), the generated client crashes when called without a body:

ValueError: No value for given attribute

The operation layer correctly computed content_type = None when no body was passed, but the generated request builder declared content_type: str = kwargs.pop("content_type") (required) and serialized the header unconditionally, so _SERIALIZER.header("content_type", None, "str") raised.

The asymmetry was the defect: the operation layer knew the body was optional, but the request-builder layer did not.

Fix

Thread body-optionality into the request-builder layer so a None content-type is omitted instead of serialized.

  • parameter_serializer.py
    • serialize_query_header now accepts is_body_optional and guards the content-type header with if content_type is not None: when the body is optional.
    • pop_kwargs_from_signature accepts an explicit is_body_optional and declares the content-type kwarg as Optional[str] = kwargs.pop("content_type", ...None) even when the param is otherwise required.
  • builder_serializer.py
    • The request builder's pop_kwargs_from_signature and serialize_headers pass is_body_optional (derived from body_parameter.optional, reliable even in dpg mode where the request-builder body isn't in the signature).

Operation-layer behavior is unchanged (its content_type = content_type if body else None reassignment is not emitted in the request builder).

Generated code (after)

def build_..._request(**kwargs: Any) -> HttpRequest:
    _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
    content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("content-type", None))
    ...
    # Construct headers
    if content_type is not None:
        _headers["content-type"] = _SERIALIZER.header("content_type", content_type, "str")
    ...

Verification

  • Reproduced the bug with a minimal spec, then confirmed the regenerated request builder now declares content_type: Optional[str] and guards the header.
  • Full regeneration of both flavors (unbranded + azure): zero generated diffs — no regressions.
  • Added tests/unit/test_content_type_optional_body.py (4 tests); full unit suite passes.

Code Diff

No code diff caused by this PR

@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 Jul 16, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 3a6fa97

@github-actions

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/http-client-python
Show changes

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

Fix generated request builders serializing a None content-type header for an,> operation with an optional body whose content-type is required/constant. The,> content-type kwarg is now declared Optional[str] and the header is omitted,> when it is None, instead of raising ValueError: No value for given attribute.

@msyyc msyyc changed the title fix: omit content-type header when optional body is absent [python] fix: omit content-type header when optional body is absent Jul 16, 2026

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

Fixes the TypeSpec Python HTTP client generator so that generated request builders omit the content-type header when the request body is optional and absent, preventing _SERIALIZER.header(..., None, ...) from raising ValueError: No value for given attribute (issue #11253).

Changes:

  • Thread is_body_optional into request-builder kwarg popping and header serialization so content_type becomes Optional[str] with a None default and the header is conditionally emitted.
  • Update request-builder serialization to derive body optionality from the modeled body parameter (including dpg scenarios where the body isn’t in the request-builder signature).
  • Add unit regression tests and a Chronus changelog entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
packages/http-client-python/generator/pygen/codegen/serializers/parameter_serializer.py Adds is_body_optional handling to treat content-type as nullable/guarded when body is optional.
packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py Passes body optionality through to request-builder kwarg/header serialization.
packages/http-client-python/tests/unit/test_content_type_optional_body.py Adds regression unit tests covering guarded header emission and optionalized content_type kwarg.
.chronus/changes/http-client-python_fix-optional-body-content-type-2026-7-16-0-0-0.md Records the generator fix in the changelog.

@azure-sdk-automation

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants