{"record":{"id":"5ec3e65dde7be0ec","repo":"payloadcms/payload","slug":"field-field-name-is-either-missing-a-field-ty","errorCode":null,"errorMessage":"Field \"${field.name}\" is either missing a field type or it does not match an available field type","messagePattern":"Field \"(.+?)\" is either missing a field type or it does not match an available field type","errorType":"exception","errorClass":"MissingFieldType","httpStatus":500,"severity":"error","filePath":"packages/payload/src/fields/config/sanitize.ts","lineNumber":168,"sourceCode":"  parentIsLocalized,\n  parentSchemaPath,\n  polymorphicJoins,\n  requireFieldLevelRichTextEditor,\n  richTextSanitizers,\n  validRelationships,\n}: SanitizeFieldArgs): SanitizeFieldResult => {\n  const result: SanitizeFieldResult = {}\n\n  if ('_sanitized' in field && field._sanitized === true) {\n    return result\n  }\n\n  if ('_sanitized' in field) {\n    field._sanitized = true\n  }\n\n  if (!field.type) {\n    throw new MissingFieldType(field)\n  }\n\n  const fieldAffectsData = _fieldAffectsData(field)\n\n  const { indexPath, schemaPath } = getFieldPaths({\n    field,\n    index,\n    parentIndexPath,\n    parentSchemaPath,\n  })\n\n  // Reserved field name checks\n  if (isTopLevelField && fieldAffectsData && field.name) {\n    if (collectionConfig && collectionConfig.upload) {\n      if (reservedBaseUploadFieldNames.includes(field.name)) {\n        throw new ReservedFieldName(field, field.name)\n      }\n    }","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/fields/config/sanitize.ts#L150-L186","documentation":"Thrown by Payload's field sanitizer (sanitize.ts:168) when a field definition has no `type` property, or a type that does not resolve to a known Payload field type. Every field in a collection, global, array, block, group, or tab must declare a `type` so Payload can derive the DB schema, admin UI, and validators. The check runs on every field during `payload.init()` / config sanitization, so this fails at boot, not at request time.","triggerScenarios":"Defining a field as `{ name: 'title' }` with no `type`; spreading a partial field object that omits type (`...baseField` where baseField has no type); returning a field from a plugin/hook that returns `{ name, label }` only; passing a UI field config that lost its `type` during a refactor.","commonSituations":"Copy-pasting a field and deleting the type line by accident; loosely typed field variables (`Field` widened to `any`); upgrading Payload where a previously-inferred type now must be explicit; building fields dynamically from a config map that forgets the type key.","solutions":["Add the `type` property to the offending field definition (e.g. `type: 'text'`).","Confirm the type string is one of Payload's known types (text, number, textarea, select, relationship, upload, array, blocks, group, tabs, date, checkbox, json, code, point, richText, email, join, ui, slug, virtual, etc.).","If the field came from a spread/import, inspect the source object to ensure it carries `type`.","If using a custom field plugin, make sure it returns a fully-formed field including `type`."],"exampleFix":"// before\nfields: [\n  { name: 'title' }\n]\n// after\nfields: [\n  { name: 'title', type: 'text' }\n]","handlingStrategy":"validation","validationCode":"import { reservedBaseUploadFieldNames } from 'payload/reservedFieldNames' // pseudocode\nconst KNOWN_TYPES = new Set(['text','number','textarea','select','relationship','upload','array','blocks','group','tabs','date','checkbox','json','code','point','richText','email','join','ui','slug','virtual','radio','password'])\nfunction assertFieldsHaveType(fields: any[], path = '') {\n  for (const f of fields) {\n    if (!f || typeof f.type !== 'string' || !KNOWN_TYPES.has(f.type)) {\n      throw new Error(`Field at ${path || 'root'} is missing a valid type: ${JSON.stringify(f?.name)}`)\n    }\n    for (const k of ['fields','tabs']) if (Array.isArray(f[k])) assertFieldsHaveType(f[k], `${path}.${f.name}.${k}`)\n    if (Array.isArray(f.blocks)) for (const b of f.blocks) if (b?.fields) assertFieldsHaveType(b.fields, `${path}.${f.name}.blocks.${b.slug}`)\n  }\n}\n// call against your config collections/globals before buildConfig():\n// collections.forEach(c => assertFieldsHaveType(c.fields, c.slug))","typeGuard":"type AnyField = { name?: string; type?: string }\nfunction hasValidType(f: AnyField): f is AnyField & { type: string } {\n  return typeof f?.type === 'string' && f.type.length > 0\n}\n// usage: if (!hasValidType(field)) throw new Error('field missing type')","tryCatchPattern":"try {\n  await payload.init({ config })\n} catch (err) {\n  if (err?.name === 'MissingFieldType' || /missing a field type/i.test(err?.message ?? '')) {\n    // err.data.field exposes the offending field object — log it to locate the config entry\n    console.error('Field missing type:', err?.data?.field)\n  }\n  throw err\n}","preventionTips":["Type your field arrays as `Field[]` (from payload) so TypeScript rejects typeless objects at compile time.","Write a small unit test that sanitizes your config in isolation and fails fast on missing types.","When spreading partial field configs, type them as `Field` so omissions surface."],"tags":["config","schema","sanitization","field","typescript","boot"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}