export type WorkflowErrorType = | "transient" | "check_failed" | "parameter_error" | "config_error" | "contract_error" | "execution_reverted" | "unknown"; export interface WorkflowErrorClassification { errorType: WorkflowErrorType; retryable: boolean; } const messageOf = (error: unknown): string => { return error instanceof Error ? error.message : String(error); }; const matchesAny = (message: string, patterns: RegExp[]): boolean => { return patterns.some((pattern) => pattern.test(message)); }; export const classifyWorkflowError = (error: unknown): WorkflowErrorClassification => { const message = messageOf(error); if (matchesAny(message, [ /timeout/i, /timed out/i, /rate limit/i, /too many requests/i, /\b429\b/, /\b503\b/, /ECONNRESET/i, /ETIMEDOUT/i, /EAI_AGAIN/i, /header not found/i, /missing trie node/i, /could not detect network/i, /network error/i, /connection/i, ])) { return { errorType: "transient", retryable: true }; } if (matchesAny(message, [ /Check failed/i, ])) { return { errorType: "check_failed", retryable: false }; } if (matchesAny(message, [ /Missing parameters/i, /Reference not found/i, /must resolve to an array/i, /Invalid admin address/i, /requires saveAs/i, /Duplicate deploy saveAs/i, ])) { return { errorType: "parameter_error", retryable: false }; } if (matchesAny(message, [ /Missing config\.yaml/i, /Missing hardhat\.config\.ts network/i, /cannot be forked/i, /no factory address/i, /no salt/i, /simulation callers?/i, /releaseTargetNetworks/i, /release-target simulation/i, ])) { return { errorType: "config_error", retryable: false }; } if (matchesAny(message, [ /Contract does not expose method/i, /missing upgradeToAndCall/i, /missing newImplementation/i, /Unsupported step kind/i, ])) { return { errorType: "contract_error", retryable: false }; } if (matchesAny(message, [ /revert/i, /reverted/i, /CALL_EXCEPTION/i, /transaction failed/i, /gate-tool validate failed/i, ])) { return { errorType: "execution_reverted", retryable: false }; } return { errorType: "unknown", retryable: false }; };