--- title: Use issue.path for Nested Error Location impact: HIGH impactDescription: Without path information, users can't identify which nested field failed; path provides exact location in complex objects tags: error, path, nested, debugging --- ## Use issue.path for Nested Error Location When validating nested objects or arrays, `issue.path` tells you exactly where the error occurred. This is essential for highlighting the correct form field or providing precise error messages in complex data structures. **Incorrect (ignoring path information):** ```typescript import { z } from 'zod' const orderSchema = z.object({ customer: z.object({ name: z.string().min(1, 'Name required'), address: z.object({ street: z.string().min(1, 'Street required'), city: z.string().min(1, 'City required'), }), }), items: z.array(z.object({ productId: z.string(), quantity: z.number().positive('Quantity must be positive'), })), }) const result = orderSchema.safeParse({ customer: { name: '', address: { street: '', city: '' } }, items: [{ productId: 'abc', quantity: -1 }], }) if (!result.success) { // Only showing message, not WHERE the error is result.error.issues.forEach(issue => { console.log(issue.message) // 'Name required', 'Street required', 'Quantity must be positive' // User: "Which quantity? Which field?" }) } ``` **Correct (using path information):** ```typescript import { z } from 'zod' const orderSchema = z.object({ customer: z.object({ name: z.string().min(1, 'Name required'), address: z.object({ street: z.string().min(1, 'Street required'), city: z.string().min(1, 'City required'), }), }), items: z.array(z.object({ productId: z.string(), quantity: z.number().positive('Quantity must be positive'), })), }) const result = orderSchema.safeParse({ customer: { name: '', address: { street: '', city: '' } }, items: [{ productId: 'abc', quantity: -1 }], }) if (!result.success) { result.error.issues.forEach(issue => { // path is an array of keys/indices console.log(`${issue.path.join('.')}: ${issue.message}`) // 'customer.name: Name required' // 'customer.address.street: Street required' // 'customer.address.city: City required' // 'items.0.quantity: Quantity must be positive' }) } ``` **Building field-specific error mapping:** ```typescript function mapErrorsToFields(error: z.ZodError) { const fieldErrors: Map = new Map() for (const issue of error.issues) { const fieldPath = issue.path.join('.') const existing = fieldErrors.get(fieldPath) ?? [] fieldErrors.set(fieldPath, [...existing, issue.message]) } return fieldErrors } // Usage const errors = mapErrorsToFields(result.error) errors.get('customer.name') // ['Name required'] errors.get('items.0.quantity') // ['Quantity must be positive'] ``` **For array items, get index from path:** ```typescript const itemsWithErrors: Set = new Set() result.error.issues.forEach(issue => { if (issue.path[0] === 'items' && typeof issue.path[1] === 'number') { itemsWithErrors.add(issue.path[1]) } }) // Highlight items at indices: Set { 0 } ``` **Using path with format():** ```typescript const formatted = result.error.format() // Access errors at any path level formatted.customer?.address?.city?._errors // ['City required'] formatted.items?.[0]?.quantity?._errors // ['Quantity must be positive'] ``` **When NOT to use this pattern:** - Flat objects where field name is obvious - When using form libraries that handle path mapping Reference: [Zod Error Handling](https://zod.dev/error-handling)