Skip to content

fix: keep nested None and passed empty lists in update payloads - #1221

Open
dheerajitbhu wants to merge 1 commit into
apify:masterfrom
dheerajitbhu:fix/clean-payload-nested-none
Open

dheerajitbhu wants to merge 1 commit into
apify:masterfrom
dheerajitbhu:fix/clean-payload-nested-none

Conversation

@dheerajitbhu

Copy link
Copy Markdown
Contributor

Closes #1136

Problem

_clean_json_payload stripped None at every nesting level and then dropped dicts that became empty. So actor.update(tagged_builds={'beta': None}) sent {} and the tag was never removed, even though the update() docstring and the API docs say null removes it. Separately, schedule.update(actions=[]) and version.update(env_vars=[], source_files=[]) turned the empty list into None before the filter, so those updates were silently ignored.

Change

  • _clean_json_payload now drops None only at the top level (an unset argument) and keeps None inside values the caller passed. Empty nested dicts are still removed at every level, because sub-models dumped with exclude_none=True produce them when all their fields are unset (e.g. exampleRunInput).
  • ScheduleClient(Async).update passes actions through as given; ActorVersionClient(Async).update only converts env_vars / source_files when they are not None, so [] is sent as [].
  • Top-level null (clearing fields like generalAccess) is out of scope here and stays with Let update() methods clear a field by sending null #1135, as the issue says.

Tests

  • test__clean_json_payload cases updated for the new contract (nested None kept, nested empty dicts removed, lists kept as passed).
  • New sync + async tests check the actual request body: tagged_builds={'beta': None} → {"taggedBuilds": {"beta": null}}, schedule.update(actions=[]) → {"actions": []}, version.update(env_vars=[], source_files=[]) → {"envVars": [], "sourceFiles": []}. 15 of them fail on master and pass with the fix.
  • Full unit suite (2622 passed), poe lint and poe type-check are green.

Note: the branch is based on 41c6f39 (6 commits behind master, none touching these files) because my fork can't receive upstream workflow-file changes; happy to rebase if needed.

Disclosure: I maintain a few Apify Store actors, and this change was prepared with AI assistance and reviewed and tested by me.

@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label Nov 10, 2026
@staticmethod
def _clean_json_payload(data: dict) -> dict:
"""Remove None values and empty nested dicts from an API request payload.
"""Remove unset top-level fields and empty nested dicts from an API request payload.

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.

Question: a nested None the caller passes is kept now, but a nested {} they pass is still removed, so tagged_builds={'beta': {}} is dropped. Is that intended? The JS client sends both as given.

✍️ Drafted by Claude Code

Comment on lines +158 to +160
A top-level `None` means the caller did not pass the argument, so the field is left out. `None` inside
a nested value the caller passed is kept and sent as `null`, so for example
`tagged_builds={'beta': None}` sends `{"taggedBuilds": {"beta": null}}` and removes the tag.

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.

Nit: the docstring can use the full 120-character line length.

Suggested change
A top-level `None` means the caller did not pass the argument, so the field is left out. `None` inside
a nested value the caller passed is kept and sent as `null`, so for example
`tagged_builds={'beta': None}` sends `{"taggedBuilds": {"beta": null}}` and removes the tag.
A top-level `None` means the caller did not pass the argument, so the field is left out. `None` inside
a nested value the caller passed is kept and sent as `null`, so for example `tagged_builds={'beta': None}`
sends `{"taggedBuilds": {"beta": null}}` and removes the tag.

✍️ Drafted by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_clean_json_payload drops nested None, so update() cannot remove a build tag or clear a field

2 participants