Skip to content
← Back to Functions
Code

Retry Dead Letter Reporter

Summarizes failed queued jobs or webhooks after retry exhaustion.

Function signature

ogBuildDeadLetterReport(failed_jobs = array(), options = array())

Categories

  • APIs and Webhooks

Parameters

failed_jobsFailed queue records.optionsReport grouping and redaction options. Recognized keys: `group_by`.

Return value

Short public-safe status message.

  • total_failed
  • groups
  • rows

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Summarizes failed queued jobs or webhooks after retry exhaustion.
 *
 * Payloads are summarized and redacted so admin screens can show useful failure details without
 * exposing secrets or full request bodies.
 *
 * @param array $failed_jobs Failed queue records.
 * @param array $options Report grouping and redaction options.
 * @return array Dead-letter report data.
 */
function ogBuildDeadLetterReport($failed_jobs = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($failed_jobs)) {
		$result['message'] = 'Failed jobs must be an array.';
		return $result;
	}
	if (!is_array($options)) {
		$options = array();
	}

	$group_by = 'type';
	if (!empty($options['group_by'])) {
		$group_by = preg_replace('/[^a-zA-Z0-9_]/', '', (string)$options['group_by']);
	}
	$groups = array();
	$rows = array();

	foreach ($failed_jobs as $job) {
		if (!is_array($job)) {
			continue;
		}
		$group_value = 'unknown';
		if (!empty($job[$group_by])) {
			$group_value = (string)$job[$group_by];
		}
		if (empty($groups[$group_value])) {
			$groups[$group_value] = array('count' => 0, 'last_failed_at' => '', 'retryable' => 0);
		}
		$groups[$group_value]['count']++;
		$failed_at = '';
		if (!empty($job['failed_at'])) {
			$failed_at = (string)$job['failed_at'];
			$groups[$group_value]['last_failed_at'] = $failed_at;
		}
		$attempts = 0;
		if (!empty($job['attempts'])) {
			$attempts = (int)$job['attempts'];
		}
		$max_attempts = 0;
		if (!empty($job['max_attempts'])) {
			$max_attempts = (int)$job['max_attempts'];
		}
		$retryable = $max_attempts <= 0 || $attempts < $max_attempts;
		if ($retryable) {
			$groups[$group_value]['retryable']++;
		}
		$id = '';
		if (!empty($job['id'])) {
			$id = (string)$job['id'];
		}
		$type = '';
		if (!empty($job['type'])) {
			$type = (string)$job['type'];
		}
		$error = '';
		if (!empty($job['error'])) {
			$error = substr((string)$job['error'], 0, 300);
		}
		$rows[] = array(
			'id' => $id,
			'type' => $type,
			'error' => $error,
			'attempts' => $attempts,
			'retryable' => $retryable,
			'failed_at' => $failed_at
		);
	}

	$result['success'] = true;
	$result['message'] = 'Dead-letter report built.';
	$result['data'] = array(
		'total_failed' => count($rows),
		'groups' => $groups,
		'rows' => $rows
	);

	return $result;
}