{"record":{"id":"aafee4797b37784b","repo":"emberjs/ember.js","slug":"attempted-to-close-a-tracking-frame-but-one-was-n","errorCode":null,"errorMessage":"attempted to close a tracking frame, but one was not open","messagePattern":"attempted to close a tracking frame, but one was not open","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@glimmer/validator/lib/tracking.ts","lineNumber":74,"sourceCode":"\nconst OPEN_TRACK_FRAMES: (Tracker | null)[] = [];\n\nexport function beginTrackFrame(debuggingContext?: string | false): void {\n  OPEN_TRACK_FRAMES.push(CURRENT_TRACKER);\n\n  CURRENT_TRACKER = new Tracker();\n\n  if (DEBUG) {\n    unwrap(debug.beginTrackingTransaction)(debuggingContext);\n  }\n}\n\nexport function endTrackFrame(): Tag {\n  let current = CURRENT_TRACKER;\n\n  if (DEBUG) {\n    if (OPEN_TRACK_FRAMES.length === 0) {\n      throw new Error('attempted to close a tracking frame, but one was not open');\n    }\n\n    unwrap(debug.endTrackingTransaction)();\n  }\n\n  CURRENT_TRACKER = OPEN_TRACK_FRAMES.pop() || null;\n\n  return unwrap(current).combine();\n}\n\nexport function beginUntrackFrame(): void {\n  OPEN_TRACK_FRAMES.push(CURRENT_TRACKER);\n  CURRENT_TRACKER = null;\n}\n\nexport function endUntrackFrame(): void {\n  if (DEBUG && OPEN_TRACK_FRAMES.length === 0) {\n    throw new Error('attempted to close a tracking frame, but one was not open');","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@glimmer/validator/lib/tracking.ts#L56-L92","documentation":"endTrackFrame pops the tracking frame stack; in DEBUG builds it throws if the stack is empty, meaning endTrackFrame was called without a matching startTrackFrame. This protects the tracker state machine (CURRENT_TRACKER / OPEN_TRACK_FRAMES) from becoming corrupted.","triggerScenarios":"Calling `endTrackFrame()` directly, or an unbalanced internal path from callers like `track`, `tag`, `getValue`, `update`, `argsTag`, or `combined` that closes a frame when none was opened (e.g. a tag/GETTER used after the tracking transaction already ended).","commonSituations":"Manual use of low-level @glimmer/validator APIs (track/startTrackFrame) with mismatched begin/end; re-entrant tag evaluation during a `cached` getter; exception in a tracked computation that skipped the matching end call leaving state inconsistent for the next call.","solutions":["Ensure every endTrackFrame has a matching startTrackFrame; prefer the high-level `track(() => ...)` helper which balances automatically.","Wrap manual frame code in try/finally so an exception inside the frame still closes it exactly once.","Check for double-calls of endTrackFrame from refactored/duplicated code paths.","If seen inside a library update, report it — it usually indicates a framework bug in tag lifecycle handling."],"exampleFix":"// before\nstartTrackFrame();\nlet tag = computeTag(); // may throw\nendTrackFrame();\n\n// after\nlet tag = track(() => computeTag()); // balanced by construction","handlingStrategy":"try-catch","validationCode":"function trackedFrameOpen() {\n  // expose from validator internals or track locally in your wrapper\n  return frameOpenCount > 0;\n}\nif (!trackedFrameOpen()) throw new Error('startTrackFrame must be called before endTrackFrame');","typeGuard":null,"tryCatchPattern":"startTrackFrame();\ntry {\n  const tag = computeTag();\n  return tag;\n} finally {\n  endTrackFrame(); // closes exactly once, even on throw\n}","preventionTips":["Prefer the high-level track(() => ...) API over manual start/endTrackFrame.","Always pair manual frames inside try/finally.","Never call endTrackFrame from multiple code paths for one frame.","Count frame depth in wrappers to assert balance in tests."],"tags":["glimmer","validator","reactivity","tracking","debug"],"backgroundTag":"unbalanced-tracking-frame","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}