{"record":{"id":"564ad996b4e0d982","repo":"siyuan-note/siyuan","slug":"the-tracked-range-owner-is-required","errorCode":null,"errorMessage":"The tracked range owner is required","messagePattern":"The tracked range owner is required","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"app/src/protyle/util/trackedRange.ts","lineNumber":1137,"sourceCode":"        const handles = ownedHandles.get(state.owner);\n        handles?.forEach(item => {\n            if (item.protyle === protyle && item.handle === handle) {\n                handles.delete(item);\n            }\n        });\n        if (handles?.size === 0) {\n            ownedHandles.delete(state.owner);\n        }\n    }\n};\n\nexport const trackRange = (protyle: IProtyle, range: Range,\n                           options: ITrackRangeOptions): ITrackedRangeHandle => {\n    if (destroyedProtyles.has(protyle)) {\n        throw new Error(\"Cannot track a range in a destroyed Protyle instance\");\n    }\n    if (!options?.owner || ![\"function\", \"object\"].includes(typeof options.owner)) {\n        throw new TypeError(\"The tracked range owner is required\");\n    }\n    if (options.affinity && ![\"after\", \"before\"].includes(options.affinity)) {\n        throw new TypeError(\"The tracked range affinity must be before or after\");\n    }\n    if (unloadingPlugins.has(options.owner)) {\n        throw new Error(\"Cannot track a range for an unloaded plugin\");\n    }\n    const rangeSnapshot = getRangeSnapshot(protyle, range);\n    if (!rangeSnapshot) {\n        throw new TypeError(\"The range must be inside one editable source block of this Protyle instance\");\n    }\n    const targetTokens = rangeSnapshot.stream.tokens.slice(rangeSnapshot.start, rangeSnapshot.end);\n    if (!range.collapsed && targetTokens.length === 0) {\n        throw new TypeError(\"The tracked range must contain semantic content\");\n    }\n    const handle = Object.freeze({id: `tracked-range-${++handleSequence}`});\n    const trackedRange = range.cloneRange();\n    const state: ITrackedRangeState = {","sourceCodeStart":1119,"sourceCodeEnd":1155,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/app/src/protyle/util/trackedRange.ts#L1119-L1155","documentation":"Thrown by trackRange when options.owner is missing or is not a function or object. The owner identifies which plugin/component owns the tracked range so ranges can be cleaned up on unload; tracking without an owner would leak. It is a TypeError because it is a malformed-argument problem, not a state problem.","triggerScenarios":"Calling trackRange(protyle, range) with no options object, options: undefined/null, or options.owner set to undefined/null/string/number/boolean.","commonSituations":"Migrating code written against an older trackRange signature without options; passing plugin.name (a string) instead of the plugin instance; forgetting the options argument entirely.","solutions":["Pass the plugin instance (or a function/object owner) as options.owner: trackRange(protyle, range, {owner: this})","If your owner is a string ID, wrap it in an object or use the plugin object itself","Add a default in your wrapper so owner is always supplied","Check the current ITrackRangeOptions signature and update call sites"],"exampleFix":"// before\ntrackRange(protyle, range, {owner: \"my-plugin\"});\n// after\ntrackRange(protyle, range, {owner: this /* plugin instance */});","handlingStrategy":"validation","validationCode":"function assertOwner(owner: unknown): asserts owner is object | ((...a: unknown[]) => unknown) {\n  if (!owner || ![\"function\", \"object\"].includes(typeof owner)) {\n    throw new TypeError(\"tracked range owner required\");\n  }\n}","typeGuard":"const hasOwner = (o: ITrackRangeOptions | undefined): o is ITrackRangeOptions & {owner: object | ((...a: unknown[]) => unknown)} =>\n  !!o && [\"function\", \"object\"].includes(typeof o.owner) && o.owner != null;","tryCatchPattern":"try {\n  trackRange(protyle, range, opts);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes(\"owner is required\")) {\n    console.error(\"trackRange called without a valid owner\");\n  } else throw e;\n}","preventionTips":["Always pass the plugin instance as owner (this inside the plugin)","Type options.owner narrowly in your wrappers so strings/undefined fail at compile time","Make owner a required property in helper functions wrapping trackRange","Re-read ITrackRangeOptions after upgrades — the options argument is newer API surface"],"tags":["protyle","arguments","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}