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

  1. Return { value: validatedValues } on success
  2. Return { issues: [{ message, path }] } on failure instead of throwing yourself
  3. 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

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


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/6091bf7d77ba4a52. Report an issue: GitHub.