source dump of claude code
at main 132 lines 4.0 kB view raw
1import type { ZodError } from 'zod/v4' 2import { AbortError, ShellError } from './errors.js' 3import { INTERRUPT_MESSAGE_FOR_TOOL_USE } from './messages.js' 4 5export function formatError(error: unknown): string { 6 if (error instanceof AbortError) { 7 return error.message || INTERRUPT_MESSAGE_FOR_TOOL_USE 8 } 9 if (!(error instanceof Error)) { 10 return String(error) 11 } 12 const parts = getErrorParts(error) 13 const fullMessage = 14 parts.filter(Boolean).join('\n').trim() || 'Command failed with no output' 15 if (fullMessage.length <= 10000) { 16 return fullMessage 17 } 18 const halfLength = 5000 19 const start = fullMessage.slice(0, halfLength) 20 const end = fullMessage.slice(-halfLength) 21 return `${start}\n\n... [${fullMessage.length - 10000} characters truncated] ...\n\n${end}` 22} 23 24export function getErrorParts(error: Error): string[] { 25 if (error instanceof ShellError) { 26 return [ 27 `Exit code ${error.code}`, 28 error.interrupted ? INTERRUPT_MESSAGE_FOR_TOOL_USE : '', 29 error.stderr, 30 error.stdout, 31 ] 32 } 33 const parts = [error.message] 34 if ('stderr' in error && typeof error.stderr === 'string') { 35 parts.push(error.stderr) 36 } 37 if ('stdout' in error && typeof error.stdout === 'string') { 38 parts.push(error.stdout) 39 } 40 return parts 41} 42 43/** 44 * Formats a Zod validation path into a readable string 45 * e.g., ['todos', 0, 'activeForm'] => 'todos[0].activeForm' 46 */ 47function formatValidationPath(path: PropertyKey[]): string { 48 if (path.length === 0) return '' 49 50 return path.reduce((acc, segment, index) => { 51 const segmentStr = String(segment) 52 if (typeof segment === 'number') { 53 return `${String(acc)}[${segmentStr}]` 54 } 55 return index === 0 ? segmentStr : `${String(acc)}.${segmentStr}` 56 }, '') as string 57} 58 59/** 60 * Converts Zod validation errors into a human-readable and LLM friendly error message 61 * 62 * @param toolName The name of the tool that failed validation 63 * @param error The Zod error object 64 * @returns A formatted error message string 65 */ 66export function formatZodValidationError( 67 toolName: string, 68 error: ZodError, 69): string { 70 const missingParams = error.issues 71 .filter( 72 err => 73 err.code === 'invalid_type' && 74 err.message.includes('received undefined'), 75 ) 76 .map(err => formatValidationPath(err.path)) 77 78 const unexpectedParams = error.issues 79 .filter(err => err.code === 'unrecognized_keys') 80 .flatMap(err => err.keys) 81 82 const typeMismatchParams = error.issues 83 .filter( 84 err => 85 err.code === 'invalid_type' && 86 !err.message.includes('received undefined'), 87 ) 88 .map(err => { 89 const typeErr = err as { expected: string } 90 const receivedMatch = err.message.match(/received (\w+)/) 91 const received = receivedMatch ? receivedMatch[1] : 'unknown' 92 return { 93 param: formatValidationPath(err.path), 94 expected: typeErr.expected, 95 received, 96 } 97 }) 98 99 // Default to original error message if we can't create a better one 100 let errorContent = error.message 101 102 // Build a human-readable error message 103 const errorParts = [] 104 105 if (missingParams.length > 0) { 106 const missingParamErrors = missingParams.map( 107 param => `The required parameter \`${param}\` is missing`, 108 ) 109 errorParts.push(...missingParamErrors) 110 } 111 112 if (unexpectedParams.length > 0) { 113 const unexpectedParamErrors = unexpectedParams.map( 114 param => `An unexpected parameter \`${param}\` was provided`, 115 ) 116 errorParts.push(...unexpectedParamErrors) 117 } 118 119 if (typeMismatchParams.length > 0) { 120 const typeErrors = typeMismatchParams.map( 121 ({ param, expected, received }) => 122 `The parameter \`${param}\` type is expected as \`${expected}\` but provided as \`${received}\``, 123 ) 124 errorParts.push(...typeErrors) 125 } 126 127 if (errorParts.length > 0) { 128 errorContent = `${toolName} failed due to the following ${errorParts.length > 1 ? 'issues' : 'issue'}:\n${errorParts.join('\n')}` 129 } 130 131 return errorContent 132}