payloadcms/payload · error · Error

Collection "${hierarchySlug}" is not a hierarchy

Error message

Collection "${hierarchySlug}" is not a hierarchy

What it means

Thrown by getHierarchyAncestry when the collection at `hierarchySlug` has no `hierarchy` config object (or it is not a plain object). The ancestry walker needs the hierarchy config - specifically `parentFieldName` - to traverse the parent chain and build breadcrumb paths; without it the operation is undefined.

Source

Thrown at packages/ui/src/utilities/getHierarchyAncestry.ts:41

 */
export async function getHierarchyAncestry({
  hierarchySlug,
  ids,
  payload,
  user,
}: GetHierarchyAncestryArgs): Promise<HierarchyAncestryResult> {
  if (ids.length === 0) {
    return { items: [] }
  }

  const collectionConfig = payload.collections[hierarchySlug]?.config
  const hierarchyConfig =
    collectionConfig?.hierarchy && typeof collectionConfig.hierarchy === 'object'
      ? collectionConfig.hierarchy
      : undefined

  if (!hierarchyConfig) {
    throw new Error(`Collection "${hierarchySlug}" is not a hierarchy`)
  }

  const parentFieldName = hierarchyConfig.parentFieldName
  const useAsTitle = collectionConfig.admin?.useAsTitle || 'id'

  // Cache for already-fetched items to avoid redundant queries
  const itemCache = new Map<number | string, Record<string, unknown>>()

  const fetchItem = async (id: number | string): Promise<null | Record<string, unknown>> => {
    const cached = itemCache.get(id)
    if (cached) {
      return cached
    }

    try {
      const item = await payload.findByID({
        id,
        collection: hierarchySlug,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Ensure the collection config declares `hierarchy: { parentFieldName, ... }` as an object.
  2. Only call getHierarchyAncestry for collections where `config.hierarchy` is an object.
  3. Guard callers (navigation, breadcrumbs) with a hierarchy-presence check before invoking.
  4. If the hierarchy was intentionally removed, delete the now-dead ancestry callsite.

Example fix

// before: 'tags' has no hierarchy config
getHierarchyAncestry({ hierarchySlug: 'tags', ids, payload })

// after: declare hierarchy on the collection
// export const Tags = {
//   slug: 'tags',
//   hierarchy: { parentFieldName: 'parent' },
//   fields: [{ name: 'parent', type: 'relationship', relationTo: 'tags' }],
// }
Defensive patterns

Strategy: type-guard

Validate before calling

const cfg = payload.collections[hierarchySlug]?.config
if (!cfg?.hierarchy || typeof cfg.hierarchy !== 'object') {
  // 'hierarchySlug' is not a hierarchy - skip the ancestry call
}

Type guard

function isHierarchyCollection(
  cfg: unknown,
): cfg is { hierarchy: Record<string, unknown> } {
  return (
    !!cfg &&
    typeof cfg === 'object' &&
    'hierarchy' in cfg &&
    typeof (cfg as { hierarchy: unknown }).hierarchy === 'object' &&
    (cfg as { hierarchy: unknown }).hierarchy !== null
  )
}

// usage:
// if (isHierarchyCollection(payload.collections[hierarchySlug]?.config)) {
//   await getHierarchyAncestry({ hierarchySlug, ids, payload })
// }

Prevention

When it happens

Trigger: Calling getHierarchyAncestry with the slug of a normal (non-hierarchy) collection; referencing a hierarchy collection after its `hierarchy` config was removed; a typo in hierarchySlug; custom code assuming any collection with a parent field is a hierarchy.

Common situations: Disabling hierarchy on a collection without updating callers, slug renames that orphan ancestry callsites, custom navigation/breadcrumb code that does not check hierarchy presence first.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/8be2d492c71718ca. Report an issue: GitHub.