{"record":{"id":"d6c6116907893eff","repo":"remix-run/remix","slug":"invalid-value-for-table-tablename","errorCode":null,"errorMessage":"Invalid value for table \"' + tableName + '\"","messagePattern":"Invalid value for table \"' \\+ tableName \\+ '\"","errorType":"validation","errorClass":"DataTableValidationError","httpStatus":null,"severity":"error","filePath":"packages/data-table/src/lib/database/write-lifecycle.ts","lineNumber":361,"sourceCode":"function hasIssues(value: unknown): value is { issues: ReadonlyArray<ValidationIssue> } {\n  return typeof value === 'object' && value !== null && 'issues' in value\n}\n\nfunction hasValue(value: unknown): value is { value: unknown } {\n  return typeof value === 'object' && value !== null && 'value' in value\n}\n\nfunction normalizeWriteObject<table extends AnyTable>(\n  table: table,\n  value: unknown,\n  operation: TableWriteOperation,\n  source?: LifecycleCallbackSource,\n): Record<string, unknown> {\n  let tableName = getTableName(table)\n  let columns = getTableColumns(table)\n\n  if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n    throw new DataTableValidationError(\n      'Invalid value for table \"' + tableName + '\"',\n      [{ message: 'Expected object' }],\n      {\n        metadata: {\n          table: tableName,\n          operation,\n          ...(source ? { source } : {}),\n        },\n      },\n    )\n  }\n\n  let output: Record<string, unknown> = {}\n\n  for (let key in value) {\n    if (!Object.prototype.hasOwnProperty.call(value, key)) {\n      continue\n    }","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table/src/lib/database/write-lifecycle.ts#L343-L379","documentation":"normalizeWriteObject requires each written value to be a plain object (not null, not an array). If the value passed to insert/update/upsert is not an object, a DataTableValidationError with the message 'Invalid value for table ... Expected object' is thrown before any hook or SQL runs.","triggerScenarios":"Passing a string, number, null, or array as the values/changes argument, e.g. create({ values: JSON.parse(body) }) where the body parses to a non-object; passing an array to a single-row API.","commonSituations":"Parsing untyped request JSON and forwarding it directly; passing FormData or URLSearchParams without conversion; accidentally passing rows[0].someField (a string) instead of rows[0].","solutions":["Ensure the values argument is a plain object mapping column names to values","Parse and shape request input before calling write APIs (e.g. Object.fromEntries(formData))","Wrap untrusted input in a validator that guarantees an object shape"],"exampleFix":"// before\nawait table.create({ values: req.body }) // body is a string\n\n// after\nlet values = JSON.parse(req.body)\nawait table.create({ values })","handlingStrategy":"type-guard","validationCode":"if (typeof values !== 'object' || values === null || Array.isArray(values)) {\n  throw new Error('values must be a plain object')\n}","typeGuard":"function isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value)\n}","tryCatchPattern":"try {\n  await table.create({ values })\n} catch (error) {\n  if (error instanceof DataTableValidationError && /Expected object/.test(error.message)) {\n    return new Response('Invalid body', { status: 400 })\n  } else throw error\n}","preventionTips":["Parse and shape request bodies before passing them to write APIs","Use isPlainObject-style guards on untrusted input","Type the values parameter as Record<string, unknown> at the boundary"],"tags":["data-table","validation","type-error","input-shape"],"backgroundTag":"input-not-an-object","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}