more use of identifiable errors, improvements to inner error rendering, and more heuristics for is-retryable-error
This commit is contained in:
parent
c8797451e3
commit
2cba0ada3c
33 changed files with 241 additions and 157 deletions
|
|
@ -21,7 +21,7 @@ export class FileWriterStream extends WritableStream<Uint8Array> {
|
|||
write: async (chunk, controller) => {
|
||||
if (file === null) {
|
||||
controller.error();
|
||||
throw new Error();
|
||||
throw new Error('file is null');
|
||||
}
|
||||
|
||||
await file.write(chunk);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ export class FastifyReplyError extends Error {
|
|||
public message: string;
|
||||
public statusCode: number;
|
||||
|
||||
constructor(statusCode: number, message: string) {
|
||||
super(message);
|
||||
constructor(statusCode: number, message: string, cause?: unknown) {
|
||||
super(message, cause ? { cause } : undefined);
|
||||
this.message = message;
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import * as crypto from 'node:crypto';
|
||||
import { parseBigInt36 } from '@/misc/bigint.js';
|
||||
import { IdentifiableError } from '../identifiable-error.js';
|
||||
|
||||
export const aidRegExp = /^[0-9a-z]{10}$/;
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ function getNoise(): string {
|
|||
}
|
||||
|
||||
export function genAid(t: number): string {
|
||||
if (isNaN(t)) throw new Error('Failed to create AID: Invalid Date');
|
||||
if (isNaN(t)) throw new IdentifiableError('6b73b7d5-9d2b-48b4-821c-ef955efe80ad', 'Failed to create AID: Invalid Date');
|
||||
counter++;
|
||||
return getTime(t) + getNoise();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { parseBigInt36 } from '@/misc/bigint.js';
|
||||
import { IdentifiableError } from '../identifiable-error.js';
|
||||
|
||||
export const aidxRegExp = /^[0-9a-z]{16}$/;
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ function getNoise(): string {
|
|||
}
|
||||
|
||||
export function genAidx(t: number): string {
|
||||
if (isNaN(t)) throw new Error('Failed to create AIDX: Invalid Date');
|
||||
if (isNaN(t)) throw new IdentifiableError('6b73b7d5-9d2b-48b4-821c-ef955efe80ad', 'Failed to create AIDX: Invalid Date');
|
||||
counter++;
|
||||
return getTime(t) + nodeId + getNoise();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ export class IdentifiableError extends Error {
|
|||
*/
|
||||
public readonly isRetryable: boolean;
|
||||
|
||||
constructor(id: string, message?: string, isRetryable = false, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
constructor(id: string, message?: string, isRetryable = false, cause?: unknown) {
|
||||
super(message, cause ? { cause } : undefined);
|
||||
this.message = message ?? '';
|
||||
this.id = id;
|
||||
this.isRetryable = isRetryable;
|
||||
|
|
|
|||
|
|
@ -7,14 +7,26 @@ import { AbortError, FetchError } from 'node-fetch';
|
|||
import { UnrecoverableError } from 'bullmq';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { CaptchaError, captchaErrorCodes } from '@/core/CaptchaService.js';
|
||||
import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
|
||||
import { ConflictError } from '@/server/SkRateLimiterService.js';
|
||||
|
||||
/**
|
||||
* Returns false if the provided value represents a "permanent" error that cannot be retried.
|
||||
* Returns true if the error is retryable, unknown (as all errors are retryable by default), or not an error object.
|
||||
*/
|
||||
export function isRetryableError(e: unknown): boolean {
|
||||
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;
|
||||
if (e instanceof CaptchaError) {
|
||||
if (e.code === captchaErrorCodes.verificationFailed) return false;
|
||||
if (e.code === captchaErrorCodes.invalidParameters) return false;
|
||||
if (e.code === captchaErrorCodes.invalidProvider) return false;
|
||||
return true;
|
||||
}
|
||||
if (e instanceof FastifyReplyError) return false;
|
||||
if (e instanceof ConflictError) return true;
|
||||
if (e instanceof UnrecoverableError) return false;
|
||||
if (e instanceof AbortError) return true;
|
||||
if (e instanceof FetchError) return true;
|
||||
|
|
|
|||
|
|
@ -5,23 +5,35 @@
|
|||
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { CaptchaError } from '@/core/CaptchaService.js';
|
||||
|
||||
export function renderInlineError(err: unknown): string {
|
||||
if (err instanceof Error) {
|
||||
const text = printError(err);
|
||||
const parts: string[] = [];
|
||||
renderTo(err, parts);
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
if (err.cause) {
|
||||
const cause = renderInlineError(err.cause);
|
||||
return `${text} [caused by]: ${cause}`;
|
||||
} else {
|
||||
return text;
|
||||
function renderTo(err: unknown, parts: string[]): void {
|
||||
parts.push(printError(err));
|
||||
|
||||
if (err instanceof AggregateError) {
|
||||
for (let i = 0; i < err.errors.length; i++) {
|
||||
parts.push(` [${i + 1}/${err.errors.length}]: `);
|
||||
renderTo(err.errors[i], parts);
|
||||
}
|
||||
}
|
||||
|
||||
return String(err);
|
||||
if (err instanceof Error) {
|
||||
if (err.cause) {
|
||||
parts.push(' [caused by]: ');
|
||||
renderTo(err.cause, parts);
|
||||
// const cause = renderInlineError(err.cause);
|
||||
// parts.push(' [caused by]: ', cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printError(err: Error): string {
|
||||
function printError(err: unknown): string {
|
||||
if (err instanceof IdentifiableError) {
|
||||
if (err.message) {
|
||||
return `${err.name} ${err.id}: ${err.message}`;
|
||||
|
|
@ -40,9 +52,21 @@ function printError(err: Error): string {
|
|||
}
|
||||
}
|
||||
|
||||
if (err.message) {
|
||||
return `${err.name}: ${err.message}`;
|
||||
} else {
|
||||
return err.name;
|
||||
if (err instanceof CaptchaError) {
|
||||
if (err.code.description) {
|
||||
return `${err.name} ${err.code.description}: ${err.message}`;
|
||||
} else {
|
||||
return `${err.name}: ${err.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (err instanceof Error) {
|
||||
if (err.message) {
|
||||
return `${err.name}: ${err.message}`;
|
||||
} else {
|
||||
return err.name;
|
||||
}
|
||||
}
|
||||
|
||||
return String(err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export class StatusError extends Error {
|
|||
public isClientError: boolean;
|
||||
public isRetryable: boolean;
|
||||
|
||||
constructor(message: string, statusCode: number, statusMessage?: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
constructor(message: string, statusCode: number, statusMessage?: string, cause?: unknown) {
|
||||
super(message, cause ? { cause } : undefined);
|
||||
this.name = 'StatusError';
|
||||
this.statusCode = statusCode;
|
||||
this.statusMessage = statusMessage;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue