Skip to content

fix: parse cursor pagination with json.loads on resume#76

Merged
anaselmhamdi merged 1 commit into
mainfrom
fix-cursor-resume-eval
Apr 20, 2026
Merged

fix: parse cursor pagination with json.loads on resume#76
anaselmhamdi merged 1 commit into
mainfrom
fix-cursor-resume-eval

Conversation

@anaselmhamdi

Copy link
Copy Markdown
Collaborator

Summary

On resume, the producer crashed with:

ValueError: malformed node or string on line 1: <ast.Name object at 0x...>
  File ".../bizon/engine/pipeline/producer.py", line 61, in get_or_create_cursor
    pagination=ast.literal_eval(cursor_from_db.pagination),

Root cause: pagination is written as JSON (json.dumps at backend/adapters/sqlalchemy/backend.py:296 and :398), but read back with ast.literal_eval. ast.literal_eval parses Python literal syntax; JSON's true / false / null become ast.Name nodes and raise. Any pagination dict containing a bool or None — e.g. {"has_more": true, "cursor": null} — was unrecoverable. The pipeline exited with BACKEND_ERROR on every retry.

Impact: Connectors that currently break on resume

  • Notionhas_more, data_sources_loaded: True, next_cursor can be None
  • Cycle — passes through GraphQL pageInfo which includes hasNextPage (bool)

Other connectors (Dummy, GSheets, Kafka, PokeAPI, SanaAI, Periscope) only store strings/lists/ints and worked by accident.

Change

  • bizon/engine/pipeline/producer.py — swap ast.literal_evaljson.loads, matching the write path. No migration needed: json.dumps has been the writer since the initial commit.
  • tests/engine/test_producer_recovery.py — new regression test test_recovery_with_json_only_pagination_values persisting {"cursor": ..., "has_more": True, "next_page": None} and asserting recovery. Fails on main with the exact bug-report stacktrace.

Test plan

  • uv run pytest tests/engine/test_producer_recovery.py -v — both tests pass
  • uv run pytest tests/engine/ --ignore=tests/engine/test_parse_yaml.py --ignore=tests/engine/queue/test_queue_factory.py — 34 passed; remaining failures are unrelated missing-extras (postgres/bigquery/kafka)
  • make format — clean
  • Verify a Notion or Cycle pipeline resumes cleanly in staging after this ships

🤖 Generated with Claude Code

Pagination is written to the backend via json.dumps but was read back
with ast.literal_eval, which can't parse JSON's true/false/null.
Connectors whose pagination payload contained a bool or None (Notion,
Cycle) could never resume — the retry hit ValueError and exited with
BACKEND_ERROR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@anaselmhamdi anaselmhamdi requested a review from aballiet April 20, 2026 17:58
@anaselmhamdi anaselmhamdi merged commit 735b505 into main Apr 20, 2026
1 check passed
@anaselmhamdi anaselmhamdi mentioned this pull request Apr 20, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant