hcengineering/platform · error

Attribute not found: ${key}, ${cardPath}

Error message

Attribute not found: ${key}, ${cardPath}

What it means

A defensive check inside createCardWithRelations: after masterTagAttributes.has(key) returns true, the map get() should never be undefined. Throwing here signals a race/modification between the has-check and the get, or a custom map implementation with inconsistent behavior.

Source

Thrown at packages/importer/src/huly/cards.ts:561

          throw new Error('Blob file not found: ' + blobPath + ' from:' + cardPath)
        }
        blobProps[blobFile._id] = {
          file: blobFile._id,
          type: blobFile.type,
          name: blobFile.name,
          size: blobFile.size,
          metadata: {} // todo: blobFile.metadata
        }
      }
      cardProps.blobs = blobProps
    }

    const relations: UnifiedDoc<Doc>[] = []
    for (const [key, value] of Object.entries(customProperties)) {
      if (masterTagAttributes.has(key)) {
        const attr = masterTagAttributes.get(key)
        if (attr === undefined) {
          throw new Error(`Attribute not found: ${key}, ${cardPath}`)
        }

        const attrProps = attr.props

        const attrType = attrProps.type
        const attrBaseType = attrType._class === core.class.ArrOf ? attrType.of : attrType
        const values = attrType._class === core.class.ArrOf ? value : [value]
        const propValues = []
        for (const val of values) {
          if (attrBaseType._class === core.class.RefTo) {
            const refPath = path.resolve(path.dirname(cardPath), val)
            const ref = this.metadataRegistry.getRef(refPath) as Ref<Card>
            propValues.push(ref)
          } else {
            propValues.push(val)
          }
        }
        cardProps[attrProps.name] = attrType._class === core.class.ArrOf ? propValues : propValues[0]

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Audit for concurrent mutation of masterTagAttributes during processing and snapshot the map before iterating cards.
  2. Ensure a genuine Map is passed (not a Map-like wrapper with divergent has/get).
  3. Log the key and map contents at failure to identify what removed the entry.

Example fix

// before
await Promise.all(cards.map(c => this.processCard(c, masterTagAttributes)))
// after — snapshot to avoid concurrent mutation
const attrsSnapshot = new Map(masterTagAttributes)
await Promise.all(cards.map(c => this.processCard(c, attrsSnapshot)))
Defensive patterns

Strategy: try-catch

Validate before calling

import fs from 'fs'
function hasMasterTagAttr(attrs: Map<string, unknown>, key: string): boolean {
  return attrs instanceof Map && attrs.has(key)
}

Type guard

function isRealMap<K, V>(m: unknown): m is Map<K, V> {
  return m instanceof Map
}

Try / catch

try {
  await processor.cardWithRelations(cardPath)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Attribute not found:')) {
    console.error('masterTagAttributes was mutated or replaced mid-import — snapshot maps before processing')
  } else throw e
}

Prevention

When it happens

Trigger: Effectively unreachable in normal use — the key was just confirmed present in masterTagAttributes. It could only fire if masterTagAttributes is mutated concurrently, is a Map-like object whose has/get disagree, or a custom subclass overrides one of the methods inconsistently.

Common situations: Custom concurrency where another async task clears/rebuilds the attributes map mid-processing; wrapping the map in a proxy or passing a plain object masquerading as a Map.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/a10c10fd80d31bec. Report an issue: GitHub.