Skip to main content

Utilities common patterns

Keep invalid input out of logs by default

zodErrorToStructuredLog accepts the rejected input as an optional second argument. Omit it unless the log destination, retention policy, and data classification permit the payload.

Fragment inside an if (!parsed.success) branch; logger is the application's structured logger:

const event = zodErrorToStructuredLog(parsed.error);
logger.info(event, "request rejected");

If the input is useful for diagnosis, pass a redacted projection rather than the original boundary value.

Fragment inside an if (!parsed.success) branch, after input has been narrowed to Record<string, unknown>; logger is the application's structured logger:

const safeInput = {
emailDomain:
typeof input.email === "string" ? input.email.split("@")[1] : undefined,
suppliedFields: Object.keys(input),
};

logger.info(
zodErrorToStructuredLog(parsed.error, safeInput),
"request rejected",
);

Select text or structure at the final boundary

Use zodErrorToString for a CLI message or compact diagnostic. Use zodErrorToStructuredLog when log processors need fields such as issueCount, path, or code.

Fragment where error is a ZodError, format is the caller's output choice, and logger is the application's structured logger:

if (format === "text") {
process.stderr.write(`${zodErrorToString(error)}\n`);
} else {
logger.error(zodErrorToStructuredLog(error));
}

Neither function changes the ZodError, maps errors into Altstack Result values, or selects an HTTP status. Those decisions belong to the caller.

Preserve Zod's issue order

Both formatters iterate error.issues in its existing order. If consumers require a different sort or grouping, transform the returned issues array after formatting rather than assuming it is grouped by path.

See also

Utilities API Documentation describes every property and the exact path normalization.