jely2002/youtube-dl-gui · error · Error
Playlist group is missing entry metadata.
Error message
Playlist group ${groupId} is missing entry metadata. What it means
expandPlaylistGroup resolves a playlist group, its leader item, and their entries metadata before expanding the playlist into individual items. If any of group, leader?.entries, or group.entries is missing, it throws 'Playlist group ${groupId} is missing entry metadata.' Playlists cannot be expanded without the entry list carried by the leader.
Solutions
- Confirm the groupId refers to a playlist group whose leader item has an entries array before calling expand.
- Check groupStore.findGroupLeader(groupId) and leader.entries in the caller and surface a user-facing 'not a playlist' message instead of throwing.
- Ensure the backend payload that creates playlist groups always includes entries on both the group and the leader.
- Catch the error where playlist selection is applied and show a toast asking the user to re-add the playlist.
Example fix
// before
await expandPlaylistGroup(groupId, selection);
// after
const leader = groupStore.findGroupLeader(groupId);
if (!leader?.entries) {
console.warn(`Group ${groupId} is not an expandable playlist`);
return;
}
await expandPlaylistGroup(groupId, selection); Defensive patterns
Strategy: validation
Validate before calling
const group = groupStore.findGroupById(groupId);
const leader = groupStore.findGroupLeader(groupId);
if (!group || !leader?.entries || !group.entries) {
throw new Error(`Group ${groupId} is not an expandable playlist`);
} Type guard
const isExpandablePlaylist = (groupId: string): boolean => {
const g = groupStore.findGroupById(groupId);
const l = groupStore.findGroupLeader(groupId);
return !!g && !!l?.entries && !!g.entries;
}; Try / catch
try {
await expandPlaylistGroup(groupId, selection);
} catch (e) {
if (e instanceof Error && e.message.includes('missing entry metadata')) {
toast.error('This item is not a valid playlist.');
return;
}
throw e;
} Prevention
- Only route playlist groups into the expansion code path.
- Assert leader.entries is present right after receiving the playlist payload from the backend.
- Avoid deleting/replacing leader items between add and expansion.
When it happens
Trigger: Calling expandPlaylistGroup (directly or via processMediaAddPayload) for a groupId that is not a playlist group, whose leader item was deleted or replaced, or whose entries metadata was never populated by the backend.
Common situations: Passing a non-playlist (single media) group ID to the playlist expansion path; the leader item was removed by the de-duplication logic in processMediaAddPayload before expansion; backend produced a playlist group without entries metadata.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Orphaned media item found during error handling.
- Orphaned media item found.
- No playlist entries match the selected range.
- No options found for group
AI-assisted analysis of jely2002/youtube-dl-gui@c402ee39c0 (2026-09-12).
Data as JSON: /api/errors/e892743207a7f2c4.
Report an issue: GitHub.
Appendix: source
Thrown at src/stores/media/media.ts:156
if (group.processed === total) {
finalizePlaylistGroup(group);
} else if (group.total === 1 && !hasPlaylistLeader) {
void notifyGroup(NotificationKind.VideoReady, group);
resolvePendingReadyGroup(group.id, [group.id]);
}
const next = total > 1 && isFirst
? MediaState.fetchingList
: MediaState.configure;
stateStore.setState(item.id, next);
}
async function expandPlaylistGroup(groupId: string, selection: PlaylistSelection) {
const group = groupStore.findGroupById(groupId);
const leader = groupStore.findGroupLeader(groupId);
const entries = group?.entries;
if (!group || !leader?.entries || !entries) {
throw new Error(`Playlist group ${groupId} is missing entry metadata.`);
}
const selectedEntries = applyPlaylistSelectionToEntries(entries, selection);
if (selectedEntries.length === 0) {
throw new Error('No playlist entries match the selected range.');
}
const spec = buildPlaylistItemsSpec(selection);
const previousOverrides = optionsStore.getOverrides(groupId);
const nextOverrides = cloneDownloadOverrides(previousOverrides) ?? {};
if (spec) {
nextOverrides.inputFilters = {
...(nextOverrides.inputFilters ?? {}),
playlistItems: spec,
};
} else if (nextOverrides.inputFilters) {
delete nextOverrides.inputFilters.playlistItems;
if (Object.keys(nextOverrides.inputFilters).length === 0) {View on GitHub (pinned to c402ee39c0)