{"record":{"id":"7e792d345ab2b6fa","repo":"tonhowtf/omniget","slug":"extension-playlist-is-bytes-over-the-byte-limit","errorCode":null,"errorMessage":"Extension playlist is {} bytes, over the {} byte limit","messagePattern":"Extension playlist is (.+?) bytes, over the (.+?) byte limit","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/extension_manifest.rs","lineNumber":86,"sourceCode":"\n/// Playlist text captured for `url`, when one exists and is still fresh.\n/// Never panics: every failure path collapses into `None`.\npub fn load_manifest_for_url(url: &str) -> Option<String> {\n    load_manifest_in(&manifest_dir(), url, SystemTime::now())\n}\n\n/// A file stamped in the future (clock skew, restored backup) counts as\n/// fresh rather than as an error.\nfn is_expired(modified: SystemTime, now: SystemTime) -> bool {\n    match now.duration_since(modified) {\n        Ok(age) => age.as_secs() > MANIFEST_TTL_SECS,\n        Err(_) => false,\n    }\n}\n\nfn store_manifest_in(dir: &Path, url: &str, text: &str, now: SystemTime) -> anyhow::Result<()> {\n    if text.len() > MAX_MANIFEST_BYTES {\n        anyhow::bail!(\n            \"Extension playlist is {} bytes, over the {} byte limit\",\n            text.len(),\n            MAX_MANIFEST_BYTES\n        );\n    }\n\n    fs::create_dir_all(dir)?;\n    prune_expired_in(dir, now);\n\n    let path = dir.join(manifest_file_name(url));\n    fs::write(&path, text)?;\n\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::PermissionsExt;\n        fs::set_permissions(&path, fs::Permissions::from_mode(0o600))?;\n    }\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/extension_manifest.rs#L68-L104","documentation":"store_manifest_in persists a fetched HLS extension playlist manifest to disk as text, but refuses to store anything larger than MAX_MANIFEST_BYTES (4 MiB). Oversized manifests are treated as suspicious/unbounded responses, so the write is rejected up front with the actual size and the limit in the message. This protects the manifest cache from pathological or attacker-influenced playlists.","triggerScenarios":"Calling store_manifest (or store_manifest_in, directly in tests) with playlist text whose byte length exceeds 4 * 1024 * 1024: a huge master playlist with thousands of variants, a media playlist with an enormous number of segments, or a misconfigured/non-HLS endpoint returning a large body that was mistakenly treated as a playlist.","commonSituations":"Pointing the downloader at a URL that returns a giant HTML page or binary blob instead of an m3u8; streams with extremely long DVR windows generating massive media playlists; hostile endpoints serving multi-gigabyte 'playlists' to exhaust disk.","solutions":["Verify the URL actually points to an m3u8 playlist; a normal playlist is far below 4 MiB, so a size breach usually means the wrong URL or a non-playlist response.","If the stream legitimately has a huge media playlist, fetch a rolling/sliding window (e.g. use the playlist's segment window or start from a recent segment) instead of storing the whole manifest.","If your use case genuinely needs larger manifests, raise MAX_MANIFEST_BYTES (extension_manifest.rs:38) after assessing disk/memory impact.","Pre-validate the manifest size client-side before calling store_manifest to give a better error to end users."],"exampleFix":"// before\nstore_manifest(url, &giant_playlist_text)?; // bails if > 4 MiB\n// after\nif giant_playlist_text.len() > MAX_MANIFEST_BYTES {\n    // trim to a recent window of segments before storing\n    giant_playlist_text = take_recent_segment_window(&giant_playlist_text, 500);\n}\nstore_manifest(url, &giant_playlist_text)?;","handlingStrategy":"validation","validationCode":"const MAX_MANIFEST_BYTES: usize = 4 * 1024 * 1024;\nif manifest_text.len() > MAX_MANIFEST_BYTES {\n    eprintln!(\"manifest too large: {} bytes\", manifest_text.len());\n} else {\n    store_manifest(url, &manifest_text)?;\n}","typeGuard":null,"tryCatchPattern":"// Rust: match on the store result and degrade gracefully\nmatch store_manifest(url, &text) {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"byte limit\") => {\n        // fetch a trimmed/sliding-window manifest instead\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Only pass URLs that actually return m3u8 content to store_manifest.","Log the response size before storing so pathological sources are visible early.","Cap segment counts fetched from live/DVR streams to keep playlists small.","Re-check MAX_MANIFEST_BYTES if your sources legitimately grow beyond 4 MiB."],"tags":["size-limit","manifest","hls","validation"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}