add [isRetryableSymbol] to begin moving away from unreliable heuristic

This commit is contained in:
Hazelnoot 2025-09-30 22:16:01 -04:00
parent 37f00b9ca1
commit 920dd5893d

View file

@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
// TODO replace all direct imports w/ the symbol
import { AbortError, FetchError } from 'node-fetch';
import { UnrecoverableError } from 'bullmq';
import { StatusError } from '@/misc/status-error.js';
@ -17,6 +18,10 @@ import { ConflictError } from '@/misc/errors/ConflictError.js';
* If the error cannot be readily identified as retryable, then recurses to the inner exception ("cause" property).
*/
export function isRetryableError(e: unknown): boolean {
if (hasRetryableSymbol(e)) {
return e[isRetryableSymbol];
}
if (e instanceof AggregateError) return e.errors.every(inner => isRetryableError(inner));
if (e instanceof StatusError) return e.isRetryable;
if (e instanceof IdentifiableError) return e.isRetryable;
@ -42,3 +47,17 @@ export function isRetryableError(e: unknown): boolean {
return true;
}
/**
* Error classes may define a gettable property with this key to directly specify retryability.
* If the property resolves to a boolean, then that value will be used.
* Returning any other value will fall back on the usual logic.
*/
export const isRetryableSymbol = Symbol('isRetryable');
function hasRetryableSymbol(obj: unknown): obj is { [isRetryableSymbol]: boolean } {
return obj != null
&& typeof(obj) === 'object'
&& isRetryableSymbol in obj
&& typeof(obj[isRetryableSymbol]) === 'boolean';
}