diff --git a/packages/common/src/converter/failure-converter.ts b/packages/common/src/converter/failure-converter.ts index fe1837054..55e1320c8 100644 --- a/packages/common/src/converter/failure-converter.ts +++ b/packages/common/src/converter/failure-converter.ts @@ -329,6 +329,17 @@ export class DefaultFailureConverter implements FailureConverter { message: String(err.message) ?? '', stackTrace: cutoffStackTrace(err.stack), cause: this.optionalErrorToOptionalFailure((err as any).cause, payloadConverter), + applicationFailureInfo: + 'errors' in err && Array.isArray(err.errors) + ? { + details: { + payloads: toPayloads( + payloadConverter, + err.errors.map((errInner) => this.optionalErrorToOptionalFailure(errInner, payloadConverter)) + ), + }, + } + : undefined, }; } diff --git a/packages/common/src/failure.ts b/packages/common/src/failure.ts index f5f0e48c2..1491c3a53 100644 --- a/packages/common/src/failure.ts +++ b/packages/common/src/failure.ts @@ -1,4 +1,5 @@ import type { temporal } from '@temporalio/proto'; +import { cutoffStackTrace } from '../lib'; import { errorMessage, isRecord, SymbolBasedInstanceOfError } from './type-helpers'; import { Duration } from './time'; import { makeProtoEnumConverters } from './internal-workflow'; @@ -412,9 +413,31 @@ export function ensureApplicationFailure(error: unknown): ApplicationFailure { return error; } + function toDetails(error: unknown): Record | undefined { + if (isRecord(error)) { + return { + message: String(error.message), + type: error.constructor?.name ?? error.name, + stack: cutoffStackTrace(String(error.stack)), + cause: toDetails(error.cause), + details: Array.isArray(error.errors) ? error.errors.map(toDetails) : undefined, + }; + } else if (error != null) { + return { message: String(error) }; + } else { + return undefined; + } + } + const message = (isRecord(error) && String(error.message)) || String(error); const type = (isRecord(error) && (error.constructor?.name ?? error.name)) || undefined; - const failure = ApplicationFailure.create({ message, type, nonRetryable: false }); + const failure = ApplicationFailure.create({ + message, + type, + cause: isRecord(error) && error.cause instanceof Error ? error.cause : undefined, + details: isRecord(error) && Array.isArray(error.errors) ? error.errors.map(toDetails) : undefined, + nonRetryable: false, + }); failure.stack = (isRecord(error) && String(error.stack)) || ''; return failure; }