jely2002/youtube-dl-gui · error · Error

Orphaned media item found during error handling.

Error message

Orphaned media item found during error handling.

What it means

processMediaFatalPayload records a fatal media error payload and then updates the owning group. If the group referenced by payload.groupId cannot be found in the group store, the store's invariants are broken (a media item exists without its parent group), so it throws 'Orphaned media item found during error handling.' This is an internal consistency guard, not an expected runtime condition.

Solutions

  1. Ensure groups are not removed from the group store while their media items can still emit fatal events (defer cleanup until pending fetches settle).
  2. In the event listener that calls processMediaFatalPayload, check groupStore.findGroupById(payload.groupId) first and skip/ignore the payload when the group is gone.
  3. Audit the code path that deletes groups (e.g. removeGroup/clearAll) to make sure it also rejects/cancels pending items so no orphaned payloads arrive afterwards.
  4. Wrap the call in try/catch as a safety net so a stale event cannot crash error handling.

Example fix

// before
mediaEvents.on('fatal', (payload) => processMediaFatalPayload(payload));
// after
mediaEvents.on('fatal', (payload) => {
  if (!groupStore.findGroupById(payload.groupId)) return; // stale event, ignore
  processMediaFatalPayload(payload);
});
Defensive patterns

Strategy: try-catch

Validate before calling

if (!groupStore.findGroupById(payload.groupId)) return; // stale fatal payload, nothing to do

Type guard

const isKnownGroup = (groupId: string): boolean => groupStore.findGroupById(groupId) !== undefined;

Try / catch

try {
  processMediaFatalPayload(payload);
} catch (e) {
  if (e instanceof Error && e.message.includes('Orphaned media item')) {
    console.warn('Ignoring fatal event for removed group', payload.groupId);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: A MediaFatal payload arrives (via backend event) whose groupId no longer resolves through groupStore.findGroupById — e.g. the group was removed/cleared while a fatal error for one of its items was still in flight, or events arrive out of order during teardown.

Common situations: Removing/cancelling a download group while its media items are mid-fetch; clearing the group store on navigation/unmount while stale backend events still deliver fatal payloads; backend/group-store state desync after an app restart.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of jely2002/youtube-dl-gui@c402ee39c0 (2026-09-12). Data as JSON: /api/errors/b02debbf86d9e38a. Report an issue: GitHub.

Appendix: source

Thrown at src/stores/media/diagnostics.ts:27

export const useMediaDiagnosticsStore = defineStore('media-diagnostics', () => {
  const diagnostics = ref<Record<string, MediaDiagnostic[]>>({});
  const fatals = ref<Record<string, MediaFatal>>({});

  const stateStore = useMediaStateStore();
  const groupStore = useMediaGroupStore();
  const mediaStore = useMediaStore();

  function processMediaDiagnosticPayload(payload: MediaDiagnostic) {
    diagnostics.value[payload.id] = diagnostics.value[payload.id] ?? [];
    diagnostics.value[payload.id].push(payload);
  }

  function processMediaFatalPayload(payload: MediaFatal) {
    fatals.value[payload.id] = payload;
    const { groupId } = payload;
    const currentState = stateStore.getGroupState(groupId);
    const group = groupStore.findGroupById(groupId);
    if (!group) throw new Error('Orphaned media item found during error handling.');
    if (currentState === MediaState.fetching || currentState === MediaState.fetchingList) {
      mediaStore.rejectPendingReadyGroup(groupId, payload.message);
    }
    group.processed++;
    group.errored++;
    const leader = groupStore.findGroupLeader(groupId);
    if (leader && leader.entries) {
      if (stateStore.getGroupState(groupId) === MediaState.fetchingList) {
        return;
      }
      // We are combined, so we only set one item to error.
      stateStore.setState(payload.id, MediaState.error);
      const itemsWithoutLeader = Object.values(group.items).filter(item => !item.isLeader);
      const allItemsAreTerminal = itemsWithoutLeader.every((item) => {
        const state = stateStore.getState(item.id);
        return state === MediaState.done || state === MediaState.error;
      });
      if (allItemsAreTerminal) {

View on GitHub (pinned to c402ee39c0)