Skip to content

fix(types): Literal[True] = True default on canceled fields silently destroys media buys when buyer omits the field #641

@bokelley

Description

@bokelley

Problem

Generated request types declare cancellation fields as:

```python
canceled: Annotated[
Literal[True],
Field(description='Cancel the entire media buy. Cancellation is irreversible...'),
] = True
```

The default `= True` means a buyer who omits `canceled` from the request gets it auto-set to `True` by Pydantic — i.e. the request is silently treated as a cancellation.

This is destructive: an `update_media_buy` call meant to change pacing or budget cancels the media buy if the caller forgot to include `canceled: false` (which can't be set to `false` anyway because the field is `Literal[True]`).

Affected fields

Four emissions across two conceptual fields:

File Class Field
`media_buy/update_media_buy_request.py` `UpdateMediaBuyRequest` `canceled`
`media_buy/package_update.py` `PackageUpdate` `canceled`
`bundled/media_buy/update_media_buy_request.py` `UpdateMediaBuyRequest` (bundled inline) `canceled`
`bundled/media_buy/update_media_buy_request.py` `PackageUpdate` (bundled inline) `canceled`

Response-side `canceled: bool | None = False` (e.g. `core/package.py:143`) is correct — that's a status indicator and the default is non-destructive.

Salesagent workaround

Salesagent applies an Optional-widening override on its side (`canceled: Literal[True] | None = None`) so the field is non-destructive when omitted. Every adopter has to do the same dance.

Proposed library fix

Change codegen output from:

```python
canceled: Annotated[Literal[True], Field(...)] = True
```

to:

```python
canceled: Annotated[Literal[True] | None, Field(...)] = None
```

Same wire shape (the field still only accepts `true` if present) but omission is no longer a cancellation. Mechanically the rewrite has two parts:

  1. Annotation: `Literal[True]` → `Literal[True] | None`
  2. Default: `= True` → `= None`

This is a separate fix class from #624 (Sequence widening). The right home is a new function in `scripts/post_generate_fixes.py`, scoped to the four emissions above (and any future cancel-style `Literal[True]` fields on request types — response status indicators stay as `bool | None = False`).

Severity

This is a footgun that silently destroys production state when a buyer omits a field. It's easy to hit accidentally because the field doesn't visually look like a destructive flag — there's no `destructive=True` annotation, no required-field error, just silent cancellation on omission.

Adopter impact

Once the SDK fix lands, salesagent (and any other adopter currently shipping the workaround) can drop their `canceled: Literal[True] | None = None` override.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions