mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(workflow): throw 4xx on variable resolution failures (#4325)
* fix(workflow): throw 4xx on variable resolution failures * Switch spread order
This commit is contained in:
@@ -61,7 +61,7 @@ import type {
|
||||
SerializableExecutionState,
|
||||
} from '@/executor/execution/types'
|
||||
import type { NormalizedBlockOutput, StreamingExecution } from '@/executor/types'
|
||||
import { hasExecutionResult } from '@/executor/utils/errors'
|
||||
import { getExecutionErrorStatus, hasExecutionResult } from '@/executor/utils/errors'
|
||||
import { Serializer } from '@/serializer'
|
||||
import { CORE_TRIGGER_TYPES, type CoreTriggerType } from '@/stores/logs/filters/types'
|
||||
|
||||
@@ -821,6 +821,7 @@ async function handleExecutePost(
|
||||
reqLogger.error(`Non-SSE execution failed: ${errorMessage}`)
|
||||
|
||||
const executionResult = hasExecutionResult(error) ? error.executionResult : undefined
|
||||
const status = getExecutionErrorStatus(error)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -835,7 +836,7 @@ async function handleExecutePost(
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status }
|
||||
)
|
||||
} finally {
|
||||
timeoutController.cleanup()
|
||||
|
||||
@@ -293,6 +293,7 @@ describe('InvalidFieldError', () => {
|
||||
expect(error.fieldPath).toBe('invalid.path')
|
||||
expect(error.availableFields).toEqual(['field1', 'field2'])
|
||||
expect(error.name).toBe('InvalidFieldError')
|
||||
expect(error.statusCode).toBe(400)
|
||||
})
|
||||
|
||||
it('should format message correctly', () => {
|
||||
|
||||
@@ -27,6 +27,8 @@ export interface BlockReferenceResult {
|
||||
}
|
||||
|
||||
export class InvalidFieldError extends Error {
|
||||
readonly statusCode = 400
|
||||
|
||||
constructor(
|
||||
public readonly blockName: string,
|
||||
public readonly fieldPath: string,
|
||||
|
||||
@@ -49,6 +49,8 @@ export function buildBlockExecutionError(details: BlockExecutionErrorDetails): E
|
||||
|
||||
const error = new Error(`${blockName}: ${errorMessage}`)
|
||||
|
||||
const innerStatusCode = readStatusCode(details.error)
|
||||
|
||||
Object.assign(error, {
|
||||
blockId: details.block.id,
|
||||
blockName,
|
||||
@@ -56,6 +58,7 @@ export function buildBlockExecutionError(details: BlockExecutionErrorDetails): E
|
||||
workflowId: details.context?.workflowId,
|
||||
timestamp: new Date().toISOString(),
|
||||
...details.additionalInfo,
|
||||
...(innerStatusCode !== undefined ? { statusCode: innerStatusCode } : {}),
|
||||
})
|
||||
|
||||
return error
|
||||
@@ -89,6 +92,25 @@ export function buildHTTPError(config: {
|
||||
return error
|
||||
}
|
||||
|
||||
function readStatusCode(value: unknown): number | undefined {
|
||||
if (!(value instanceof Error)) return undefined
|
||||
const status = (value as unknown as { statusCode?: unknown }).statusCode
|
||||
return typeof status === 'number' ? status : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an execution error to an HTTP status code. Errors thrown from the
|
||||
* executor that represent workflow-author mistakes (invalid field references,
|
||||
* etc.) carry a 4xx `statusCode`; everything else is a 500.
|
||||
*/
|
||||
export function getExecutionErrorStatus(error: unknown): number {
|
||||
const status = readStatusCode(error)
|
||||
if (status !== undefined && status >= 400 && status < 500) {
|
||||
return status
|
||||
}
|
||||
return 500
|
||||
}
|
||||
|
||||
export function normalizeError(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return error.message
|
||||
|
||||
@@ -97,7 +97,6 @@ export class BlockResolver implements Resolver {
|
||||
if (fallback !== undefined) {
|
||||
return fallback
|
||||
}
|
||||
throw new Error(error.message)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user