{"record":{"id":"f8f549e6e8eef14d","repo":"hcengineering/platform","slug":"update-is-null","errorCode":null,"errorMessage":"update is null","messagePattern":"update is null","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"models/activity/src/migration.ts","lineNumber":306,"sourceCode":"    let newValue: any\n    if (Array.isArray(oldValue)) {\n      newValue = []\n      for (const a of oldValue as any[]) {\n        const newA = a != null ? await getUpdatedVal(a, au.attrKey) : a\n        if (newA !== a) {\n          changed = true\n        }\n        newValue.push(newA)\n      }\n    } else {\n      newValue = await getUpdatedVal(oldValue, au.attrKey)\n      if (newValue !== oldValue) {\n        changed = true\n      }\n    }\n\n    if (changed) {\n      if (update == null) throw new Error('update is null')\n\n      update[field] = newValue\n    }\n  }\n\n  const iterator = await client.traverse(DOMAIN_ACTIVITY, {\n    _class: activity.class.DocUpdateMessage,\n    action: 'update',\n    'attributeUpdates.attrClass': 'core:class:Account'\n  })\n\n  try {\n    let processed = 0\n    while (true) {\n      const docs = await iterator.next(200)\n      if (docs === null || docs.length === 0) {\n        break\n      }","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/models/activity/src/migration.ts#L288-L324","documentation":"During activity-domain field migration, migrateField computes a new value and, when the value changed, needs the surrounding document update object to write update[field] = newValue. If that update object is null, a programmer/invariant error is thrown because migrations can never apply a change without a target update. This protects against silently dropping migration changes.","triggerScenarios":"migrateAccountsInDocUpdates iterates client.traverse(DOMAIN_ACTIVITY) doc updates and calls migrateField for each field; migrateField is invoked with update=null (or the update was destructured/lost) while the migration actually changes a value (changed=true).","commonSituations":"Custom migration code that reuses migrateField outside the doc-update loop; traverse returning a doc with no update payload; a refactor renaming the update parameter.","solutions":["Ensure migrateAccountsInDocUpdates always passes the current doc's update object into migrateField.","Add a null check before calling migrateField, skipping docs without an update payload.","If you call migrateField from custom code, construct or fetch the update object before invoking it."],"exampleFix":"// before\nawait migrateField(client, doc, field, null, oldValue)\n// after\nif (update != null) {\n  await migrateField(client, doc, field, update, oldValue)\n}","handlingStrategy":"type-guard","validationCode":"if (update == null) return // skip docs without an update payload before migrating fields","typeGuard":"function hasUpdate(u: unknown): u is Record<string, any> {\n  return u != null && typeof u === 'object'\n}","tryCatchPattern":"try {\n  await migrateAccountsInDocUpdates(client)\n} catch (e) {\n  if (e.message === 'update is null') {\n    console.error('doc update payload missing during activity migration; skipping doc')\n  } else throw e\n}","preventionTips":["Never call migrateField outside the doc-update iteration loop","Assert update != null in tests for migration code paths","Keep the update parameter plumbed explicitly, not via optional globals"],"tags":["migration","null-check","invariant"],"backgroundTag":"null-update-object","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}