Repository navigation
Description
How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.71.0 (same on 2.48.0)
Steps to Reproduce
import sentry_sdk
from sentry_sdk.crons import capture_checkin
from sentry_sdk.transport import Transport
# 1. no active client
print(sentry_sdk.capture_event({"message": "x"})) # None
print(capture_checkin(monitor_slug="nightly", status="in_progress")) # '19fe1f76b846410381dd7937594d1ceb'
# 2. check-in dropped by before_send
sent = []
class RecordingTransport(Transport):
def capture_envelope(self, envelope):
sent.append(envelope)
sentry_sdk.init(dsn="https://public@example.invalid/1", transport=RecordingTransport,
before_send=lambda event, hint: None)
print(capture_checkin(monitor_slug="nightly", status="in_progress")) # '396229524fde41ce9cf4755cae73da0c'
sentry_sdk.flush()
print(len(sent)) # 0Expected Result
The caller can tell that the check-in didn't go out, the same way capture_event returns None when the event is dropped. For example, capture_checkin returns None in that case.
Actual Result
capture_checkin ignores the return value of capture_event and always returns the generated (or passed-in) check_in_id.
This matters when the check-in is opened in one process and closed in another. We run scheduled Dramatiq jobs: the scheduler opens the in_progress check-in and passes the id to the worker in the message, and the worker opens its own check-in only when no id arrives. When the scheduler has no active client, the worker still gets an id, skips opening its own, and closes a check-in that never reached Sentry. We now check sentry_sdk.get_client().is_active() before calling capture_checkin, but that doesn't cover a check-in dropped in before_send. The Celery Beat integration hands the id over the same way (sentry-monitor-check-in-id).
A possible change: return None from capture_checkin when capture_event returns None, so the return type becomes Optional[str]. monitor and the Celery Beat integration keep working, because capture_checkin(check_in_id=None) generates a fresh id. One existing test would change: test_capture_checkin_sdk_not_initialized asserts that the passed-in id comes back when the SDK is not initialized. I have this change with tests ready locally (tests/test_crons.py and the Celery Beat crons tests pass, mypy is clean) and can open a PR if the approach is OK with you.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsWaiting for: Product Owner
Activity
linear-code commentedon Oct 3, 2026
PY-2924
ericapisani commentedon Oct 5, 2026
Hi @HardMax71 ,
Thanks for reaching out! This looks to be a reasonable approach, feel free to open the PR when you're ready.
HardMax71 commentedon Oct 5, 2026
@ericapisani thank you :)
Draft PR is opened - #7868
fix(crons): Return `None` from `capture_checkin` when nothing is sent (…