n8n-io/n8n · warning

output.error.errors[0]

Error message

output.error.errors[0]

What it means

Not a thrown error but the 400 response body returned by the controller registry when a request `body` or `query` fails Zod validation against the route's `@Body`/`@Query` DTO. The registry calls `paramType.safeParse(req[arg.type])`; on failure it returns `output.error.errors[0]` — the first Zod issue object — as JSON with HTTP 400. The literal message field is the dynamic property name `output.error.errors[0]`.

Source

Thrown at packages/cli/src/controller.registry.ts:110

			const handler = async (req: Request, res: Response) => {
				if (route.cors) {
					const corsService = Container.get(CorsService);
					const corsOptions = route.cors === true ? {} : route.cors;
					corsService.applyCorsHeaders(req, res, corsOptions);
				}

				const args: unknown[] = [req, res];
				for (let index = 0; index < route.args.length; index++) {
					const arg = route.args[index];
					if (!arg) continue;
					if (arg.type === 'param') args.push(req.params[arg.key]);
					else if (['body', 'query'].includes(arg.type)) {
						const paramType = argTypes[index] as ZodClass;
						if (paramType && 'safeParse' in paramType) {
							const output = paramType.safeParse(req[arg.type]);
							if (output.success) args.push(output.data);
							else {
								return res.status(400).json(output.error.errors[0]);
							}
						}
					} else throw new UnexpectedError('Unknown arg type: ' + arg.type);
				}
				return await controller[handlerName](...args);
			};

			const bodyArgIdx = route.args.findIndex((arg) => arg?.type === 'body');
			const bodyArgType = bodyArgIdx !== -1 ? (argTypes[bodyArgIdx] as ZodClass) : undefined;

			const middlewares = this.buildMiddlewares(route, controllerMiddlewares, bodyArgType);
			const finalHandler = route.usesTemplates
				? async (req: Request, res: Response) => {
						await handler(req, res);
					}
				: send(handler);

			router[route.method](route.path, ...middlewares, finalHandler);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Inspect the returned `errors[0]` object — it carries `path`, `code`, and `message` identifying the offending field.
  2. Compare your payload against the route's DTO in `@n8n/api-types`.
  3. Update the client to match the current schema, or upgrade/downgrade to a matching backend version.

Example fix

// before: 400 because `name` missing
fetch('/rest/v1/projects', { method:'POST', body: JSON.stringify({}) })
// after
fetch('/rest/v1/projects', { method:'POST', body: JSON.stringify({ name: 'My Project' }) })
Defensive patterns

Strategy: validation

Validate before calling

const parsed = Dto.safeParse(payload);
if (!parsed.success) {
  const issue = parsed.error.issues[0];
  throw new Error(`Validation failed at ${issue.path.join('.')}: ${issue.message}`);
}

Try / catch

try { await api.post('/x', payload); } catch (e) { if (e.response?.status === 400 && e.response.data?.path) { console.error('Fix field', e.response.data.path); } else throw e; }

Prevention

When it happens

Trigger: POSTing/PATCHing to any REST endpoint whose handler declares a Zod DTO and the payload violates the schema (missing required field, wrong type, failed regex, out-of-range number).

Common situations: Client sends a stale DTO shape after a backend version bump; missing required fields; wrong enum value; string sent where a number is expected.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/967ed43cccddca81. Report an issue: GitHub.