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
- Ensure groups are not removed from the group store while their media items can still emit fatal events (defer cleanup until pending fetches settle).
- In the event listener that calls processMediaFatalPayload, check groupStore.findGroupById(payload.groupId) first and skip/ignore the payload when the group is gone.
- 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.
- 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
- Never delete a group while its media items can still emit backend events.
- Reject pending items before clearing the group store.
- Guard event handlers with an existence check before store mutation.
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
- Orphaned media item found.
- Playlist group is missing entry metadata.
- No options found for group
- Unknown shortcut action
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)