Skip to content

Commit 88d28b0

Browse files
vdusekclaude
andcommitted
fix: treat empty APIFY_ACTOR_PRICING_INFO env var as no pricing info
The platform sets APIFY_ACTOR_PRICING_INFO to `{}` for Actors without a configured pricing model. The previous normalizer injected default fields but left the `pricingModel` discriminator missing, causing the apify-client pydantic union to fail with `union_tag_not_found` and every Actor run to crash on startup with a ValidationError. Treat an empty JSON object (and already-parsed non-dict values) as "no pricing info" so the union resolves to `None`, matching the pre-v3 behavior where `data or None` handled the same case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a8656c6 commit 88d28b0

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

src/apify/_configuration.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,20 @@ def _normalize_actor_pricing_info(data: Any) -> Any:
7676
7777
The platform-provided env var omits some fields that are required by the apify-client pydantic models
7878
(`apifyMarginPercentage`, `createdAt`, `startedAt`, and per-event `eventDescription`). Inject safe
79-
defaults for those so validation succeeds on the Actor side.
79+
defaults for those so validation succeeds on the Actor side. Treat an empty env value or an empty/
80+
discriminator-less JSON object as "no pricing info" to match the platform's behavior for Actors
81+
without a configured pricing model.
8082
"""
8183
if data is None or data == '':
8284
return None
8385
pricing_info = json.loads(data) if isinstance(data, str) else data
8486
if not isinstance(pricing_info, dict):
87+
# Already a parsed pydantic model (or some other non-dict) - pass through for pydantic to validate.
8588
return pricing_info
89+
if not pricing_info.get('pricingModel'):
90+
# Platform sets `APIFY_ACTOR_PRICING_INFO={}` for Actors without a configured pricing model;
91+
# without the discriminator, treat it as absent rather than letting the pydantic union fail.
92+
return None
8693

8794
pricing_info.setdefault('apifyMarginPercentage', 0.0)
8895
pricing_info.setdefault('createdAt', '1970-01-01T00:00:00.000Z')

tests/unit/actor/test_configuration.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,17 @@ def test_actor_pricing_info_env_var_tolerates_platform_omissions(monkeypatch: py
362362
assert config.actor_pricing_info.pricing_model == 'PAY_PER_EVENT'
363363

364364

365+
@pytest.mark.parametrize('env_value', ['', '{}'])
366+
def test_actor_pricing_info_env_var_empty_becomes_none(monkeypatch: pytest.MonkeyPatch, env_value: str) -> None:
367+
"""Platform sends `APIFY_ACTOR_PRICING_INFO={}` for Actors without a pricing model.
368+
369+
Without a `pricingModel` discriminator, the pydantic union cannot resolve — treat it as no pricing info.
370+
"""
371+
monkeypatch.setenv('APIFY_ACTOR_PRICING_INFO', env_value)
372+
config = ApifyConfiguration()
373+
assert config.actor_pricing_info is None
374+
375+
365376
def test_actor_storage_json_env_var(monkeypatch: pytest.MonkeyPatch) -> None:
366377
"""Test that actor_storages_json is parsed from JSON env var."""
367378
datasets = {'default': 'default_dataset_id', 'custom': 'custom_dataset_id'}

0 commit comments

Comments
 (0)