{"record":{"id":"2d6867937439a1b7","repo":"hcengineering/platform","slug":"lookup-class-does-not-provided-for-key","errorCode":null,"errorMessage":"lookup class does not provided for ${key}","messagePattern":"lookup class does not provided for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/view-resources/src/utils.ts","lineNumber":849,"sourceCode":"  } else {\n    try {\n      const attribute = client.getHierarchy().getAttribute(lookupClass, key.key)\n      return attribute.label\n    } catch {\n      console.log('attribute not found for ' + key.key + ' in class ' + lookupClass)\n      return getEmbeddedLabel(key.key)\n    }\n  }\n}\n\nexport function getLookupClass<T extends Doc> (\n  key: string,\n  lookup: Lookup<T>,\n  parent: Ref<Class<T>>\n): [Ref<Class<Doc>>, Ref<Class<Doc>>, boolean] {\n  const _class = getLookup(key, lookup, parent)\n  if (_class === undefined) {\n    throw new Error('lookup class does not provided for ' + key)\n  }\n  return _class\n}\n\nexport function getLookupProperty (key: string): [string, string] {\n  const parts = key.split('$lookup')\n  const lastPart = parts[parts.length - 1]\n  const split = lastPart.split('.').filter((p) => p.length > 0)\n  const prev = split.shift() ?? ''\n  const result = split.join('.')\n  return [result, prev]\n}\n\nfunction getLookup (\n  key: string,\n  lookup: Lookup<any>,\n  parent: Ref<Class<Doc>>\n): [Ref<Class<Doc>>, Ref<Class<Doc>>, boolean] | undefined {","sourceCodeStart":831,"sourceCodeEnd":867,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/view-resources/src/utils.ts#L831-L867","documentation":"getLookupClass resolves the target class for a $lookup key via getLookup(key, lookup, parent). If the lookup map has no entry for the key, the class is undefined and the function throws, since downstream lookup presenters need a concrete class.","triggerScenarios":"Calling model-building code with a lookup key whose suffix (after '$lookup') has no matching property in the Lookup object; passing an empty/wrong Lookup for the parent class.","commonSituations":"Lookup data not loaded for the class; key suffix typo (e.g. 'attachedTo' vs 'attachedTo.'); lookup defined on a different parent class in the hierarchy; nested $lookup keys whose intermediate class lookup is absent.","solutions":["Ensure the Lookup object passed contains an entry for the key suffix","Fix key spelling so getLookupProperty(key) matches the lookup property name","Load lookup data (client lookup API) for the parent class before building the model"],"exampleFix":"// before\ngetLookupClass('attachedTo.$lookup', {}, taskClass) // throws\n// after\nconst lookup: Lookup<Task> = { attachedTo: { _class: 'contact::Person' } }\ngetLookupClass('attachedTo.$lookup', lookup, taskClass)","handlingStrategy":"validation","validationCode":"const suffix = key.split('$lookup')[1]?.split('.')[1] ?? key.split('$lookup')[1]\nif (getLookup(key, lookup, parent) === undefined) {\n  throw new Error('no lookup entry for key: ' + key)\n}","typeGuard":"function hasLookupEntry<T extends Doc>(key: string, lookup: Lookup<T>, parent: Ref<Class<T>>): boolean {\n  return getLookup(key, lookup, parent) !== undefined\n}","tryCatchPattern":"try {\n  const cls = getLookupClass(key, lookup, parent)\n} catch (err) {\n  if (String(err.message).startsWith('lookup class does not provided')) {\n    const lookup = await client.loadLookup(parent, [key])\n    return getLookupClass(key, lookup, parent)\n  }\n  throw err\n}","preventionTips":["Load lookup data for the class before any $lookup model building","Keep key suffixes consistent with lookup property names","Write one helper that always pairs keys with their lookup map"],"tags":["lookup","view-model","missing-config","validation"],"backgroundTag":"missing-lookup-definition","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}