{"record":{"id":"19386c5db6f132cc","repo":"hcengineering/platform","slug":"process-error-emptyrelatedobjectvalue","errorCode":"process.error.EmptyRelatedObjectValue","errorMessage":"Empty related object {parent} value: {attr}","messagePattern":"Empty related object (.+?) value: (.+?)","errorType":"exception","errorClass":"ProcessError","httpStatus":null,"severity":"error","filePath":"server-plugins/process-resources/src/utils.ts","lineNumber":162,"sourceCode":"  if (target.length === 0) throw processError(process.error.RelatedObjectNotFound, {}, { attr: attr.label })\n  const realKey = resolveAttributeId(_process, context.key)\n  const nested = control.client.getHierarchy().findAttribute(targetClass, realKey)\n  if (context.sourceFunction !== undefined) {\n    const transform = control.client.getModel().findObject(context.sourceFunction.func)\n    if (transform === undefined) {\n      throw processError(process.error.MethodNotFound, { methodId: context.sourceFunction.func }, {}, true)\n    }\n    if (!control.client.getHierarchy().hasMixin(transform, serverProcess.mixin.FuncImpl)) {\n      throw processError(process.error.MethodNotFound, { methodId: context.sourceFunction.func }, {}, true)\n    }\n    const funcImpl = control.client.getHierarchy().as(transform, serverProcess.mixin.FuncImpl)\n    const f = await getResource(funcImpl.func)\n    const reduced = await f(target, {}, control, execution)\n    const val = Array.isArray(reduced)\n      ? reduced.map((v) => getValue(control, execution, context.key, v))\n      : getValue(control, execution, context.key, reduced)\n    if (val == null) {\n      throw processError(\n        process.error.EmptyRelatedObjectValue,\n        {},\n        { parent: attr.label, attr: nested?.label ?? getEmbeddedLabel(context.key) }\n      )\n    }\n    return val\n  }\n  const val = Array.isArray(nestedValue)\n    ? target.map((v) => getValue(control, execution, context.key, v))\n    : getValue(control, execution, context.key, target[0])\n  if (val == null) {\n    throw processError(\n      process.error.EmptyRelatedObjectValue,\n      {},\n      { parent: attr.label, attr: nested?.label ?? getEmbeddedLabel(context.key) }\n    )\n  }\n  return val","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server-plugins/process-resources/src/utils.ts#L144-L180","documentation":"The source function executed successfully but the computed value for the requested nested attribute was null/undefined. The library requires context values to be non-empty, so it throws EmptyRelatedObjectValue with the parent attribute label and the nested attribute (or embedded key).","triggerScenarios":"getContextValue -> getNestedValue: after running the sourceFunction transform and mapping getValue over the result, the resulting val is null (attribute missing on the transformed objects or transform returns nothing).","commonSituations":"Transform function returns objects without the requested key; optional/nested key path resolves to undefined on real data; misconfigured context.key that does not exist on the transform output.","solutions":["Check that the objects returned by the source function actually contain context.key and that the key is correct","Make the transform return a sensible default instead of null for missing keys","Guard the process template so the attribute is optional or provide a fallback value","Fix data so the related objects are fully populated"],"exampleFix":"// before\nconst reduced = await f(target, {}, control, execution) // objects lack 'total'\n// after\n// in the source function\nreturn items.map((i) => ({ ...i, total: i.total ?? 0 }))","handlingStrategy":"fallback","validationCode":"const probe = await f(target, {}, control, execution)\nconst probeVal = Array.isArray(probe) ? probe.map((v) => v?.[ctx.key]) : probe?.[ctx.key]\nif (probeVal == null || probeVal.every((v) => v == null)) throw new Error('Transform yields empty value')","typeGuard":"function hasValue(v: unknown): v is NonNullable<unknown> {\n  return v != null\n}","tryCatchPattern":"try {\n  val = await getContextValue(control, execution, ctx)\n} catch (err) {\n  if ((err as any)?.code === 'process.error.EmptyRelatedObjectValue') val = defaultValue\n  else throw err\n}","preventionTips":["Return defaults from transform functions instead of undefined for missing keys","Ensure context.key exists on the transform output shape (unit-test transforms)","Mark context attributes as optional in the template when data may be missing","Populate required fields at object creation time"],"tags":["process","empty-value","data"],"backgroundTag":"empty-related-object-value","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}