remix-run/remix · error · DataTableValidationError
Invalid validator result for table "' + tableName + '"
Error message
Invalid validator result for table "' + tableName + '"
What it means
The per-table validator must return either { value } (validated, possibly-coerced values) or { issues } (validation failures). Any other return shape triggers a DataTableValidationError during validateWriteValues. This distinguishes 'validator forgot to return' from an actually-passing validation.
Source
Thrown at packages/data-table/src/lib/database/write-lifecycle.ts:327
let validator = getTableValidator(table)
if (!validator) {
return normalizedInput
}
let validationResult = validator({
operation,
tableName,
value: normalizedInput as Partial<TableRow<table>>,
})
assertSynchronousCallbackResult(tableName, operation, 'validate', validationResult)
if (hasIssues(validationResult)) {
throwValidationIssues(tableName, validationResult.issues, operation, 'validate')
}
if (!hasValue(validationResult)) {
throw new DataTableValidationError(
'Invalid validator result for table "' + tableName + '"',
[{ message: 'Expected validator to return { value } or { issues }' }],
{
metadata: {
table: tableName,
operation,
source: 'validate',
},
},
)
}
return normalizeWriteObject(table, validationResult.value, operation, 'validate')
}
function hasIssues(value: unknown): value is { issues: ReadonlyArray<ValidationIssue> } {
return typeof value === 'object' && value !== null && 'issues' in value
}View on GitHub (pinned to 9696913134)
Solutions
- Return { value: validatedValues } on success
- Return { issues: [{ message, path }] } on failure instead of throwing yourself
- If wrapping a schema library, map its result: parse.ok ? { value: parse.data } : { issues: parse.error.issues }
Example fix
// before
validator: (values) => {
mySchema.parse(values) // throws or returns undefined
}
// after
validator: (values) => {
let parsed = mySchema.safeParse(values)
return parsed.success
? { value: parsed.data }
: { issues: parsed.error.issues }
} Defensive patterns
Strategy: type-guard
Type guard
function isValidValidatorResult(result: unknown): boolean {
return (
typeof result === 'object' &&
result !== null &&
('value' in result || 'issues' in result)
)
} Try / catch
try {
await table.create({ values })
} catch (error) {
if (error instanceof DataTableValidationError) {
// error.issues contains per-field problems to show the user
return json({ errors: error.issues }, { status: 400 })
}
throw error
} Prevention
- Adapt schema-library results to the { value } / { issues } contract explicitly
- Unit-test validators with both valid and invalid inputs
- Never let a validator fall through without returning
When it happens
Trigger: A validator function that returns undefined, returns the plain values object, or returns a Zod-style result without adapting it to the { value } / { issues } contract.
Common situations: Using a schema library inside the validator and returning its raw result; refactoring validators from async/safeParse patterns; forgetting that in-place mutation is not enough.
Related errors
- Invalid afterRead callback result for table "' + tableName +
- Invalid beforeDelete callback result for table "' + context.
- Invalid beforeWrite callback result for table "' + tableName
- upsert requires at least one value
- insertMany() requires at least one explicit value across the
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/6091bf7d77ba4a52.
Report an issue: GitHub.