Skip to content

Cron checkins randomly have timeout status #18124

@sstepanchuk

Description

@sstepanchuk

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/node

SDK Version

10.23.0

Framework Version

No response

Link to Sentry event

No response

Reproduction Example/SDK Setup

problem with Sentry.captureCheckIn (additionaly Sentry.withMonitor), when we have super fast cronjobs

it's sync method, but under the hood it use async method

client.sendEnvelope(envelope) // without any await !!!!

and it's causes this situation

sentry server can firstly apply finish checkin and then in_progres check in

and this situation causes random Timeout errors in sentry cron dashboard

Steps to Reproduce

just try run Sentry.withMonitor without any logic inner

and sometimes you will see this timeout error

Expected Result

Okay status on every checkins

Actual Result

Timeout status on some checkins

Additional Context

Here is my solution to this, just make captureCheckIn async, we will know when in progress check in sended, so we can send finish checkin

/**
 * Custom implementation of captureCheckIn with await sendEnvelope.
 * Based on Sentry's ServerRuntimeClient.captureCheckIn implementation.
 *
 * Key differences from Sentry.captureCheckIn():
 * - Uses await client.sendEnvelope() instead of fire-and-forget
 * - Ensures check-in is sent before continuing execution
 * - Errors propagate to caller (no silent failures)
 *
 * @see https://github.com/getsentry/sentry-javascript/blob/d4a2b2bc93f5d2be3d1b960f89b61e2e08e58ca4/packages/core/src/server-runtime-client.ts#L89-L151
 */
async function captureCheckInWithAwait(
  checkIn: CheckIn,
  monitorConfig?: MonitorConfig,
): Promise<string> {
  const client = Sentry.getClient();
  if (!client) {
    console.warn('[Sentry] Client not initialized, skipping check-in');
    return '';
  }

  const id = 'checkInId' in checkIn && checkIn.checkInId ? checkIn.checkInId : uuid4();

  // Get client options
  const options = client.getOptions();
  const { release, environment, tunnel } = options;

  // Build serialized check-in
  const serializedCheckIn: SerializedCheckIn = {
    check_in_id: id,
    monitor_slug: checkIn.monitorSlug,
    status: checkIn.status,
    release,
    environment,
  };

  // Add duration if present
  if ('duration' in checkIn) {
    serializedCheckIn.duration = checkIn.duration;
  }

  // Add monitor config if provided (for upsert)
  if (monitorConfig) {
    serializedCheckIn.monitor_config = {
      schedule: monitorConfig.schedule,
      checkin_margin: monitorConfig.checkinMargin,
      max_runtime: monitorConfig.maxRuntime,
      timezone: monitorConfig.timezone,
      failure_issue_threshold: monitorConfig.failureIssueThreshold,
      recovery_threshold: monitorConfig.recoveryThreshold,
    };
  }

  // Get trace context and dynamic sampling context from current scope
  // Implementation based on Sentry's _getTraceInfoFromScope
  const scope = getCurrentScope();
  const [dynamicSamplingContext, traceContext] = withScope(scope, () => {
    const span = getActiveSpan();
    const trace = span ? spanToTraceContext(span) : getTraceContextFromScope(scope);
    const dsc = span ? getDynamicSamplingContextFromSpan(span) : getDynamicSamplingContextFromScope(client, scope);
    return [dsc, trace];
  });

  // Add trace context to check-in if available
  if (traceContext) {
    serializedCheckIn.contexts = {
      trace: traceContext,
    };
  }

  // Create envelope with check-in data
  const envelope = createCheckInEnvelope(
    serializedCheckIn,
    dynamicSamplingContext,
    client.getSdkMetadata(),
    tunnel,
    client.getDsn(),
  );

  // KEY DIFFERENCE: await sendEnvelope instead of fire-and-forget
  // No error handling - let errors propagate to caller
  await client.sendEnvelope(envelope);

  return id;
}

Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions