{"record":{"id":"c46cedb2a93c4d25","repo":"tonhowtf/omniget","slug":"informe-o-perfil-usuario-ou-a-url-da-cole-o","errorCode":null,"errorMessage":"informe o perfil (@usuario) ou a URL da coleção","messagePattern":"informe o perfil \\(@usuario\\) ou a URL da coleção","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/tiktok/favorites.rs","lineNumber":433,"sourceCode":"            None,\n            Some(format!(\"{} itens\", out.len())),\n        );\n        if vazio || !has_more || next.is_empty() || next == cursor || out.len() as u32 >= teto {\n            break;\n        }\n        cursor = next;\n    }\n    if out.is_empty() {\n        return Err(anyhow!(\n            \"a lista voltou vazia — o TikTok não entregou os favoritos para esta sessão\"\n        ));\n    }\n    Ok(out)\n}\n\npub async fn run(opts: &Options, progress: ProgressFn) -> Result<FavoritesResult> {\n    if opts.user.trim().is_empty() {\n        return Err(anyhow!(\"informe o perfil (@usuario) ou a URL da coleção\"));\n    }\n    if opts.dest.trim().is_empty() {\n        return Err(anyhow!(\"escolha a pasta de destino\"));\n    }\n    let dest = PathBuf::from(&opts.dest);\n    std::fs::create_dir_all(&dest)?;\n\n    let session = TempCookies::new(opts.session_netscape.as_deref());\n    let used_session = session.is_some();\n    let cookies: Option<PathBuf> = opts\n        .cookies\n        .as_deref()\n        .filter(|c| !c.trim().is_empty())\n        .map(PathBuf::from)\n        .or_else(|| session.path().map(|p| p.to_path_buf()));\n\n    let handle = handle_of(&opts.user);\n    let pacer = Pacer::new(opts.delay_ms);","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/tiktok/favorites.rs#L415-L451","documentation":"The TikTok favorites downloader requires either a profile (@username) or a collection URL to know which favorites to fetch. `run` validates `opts.user` up front and aborts with this Portuguese-language message when it is empty or only whitespace. It is a deliberate fail-fast guard so the tool never starts a download with no target.","triggerScenarios":"Calling `run(opts, progress)` with `opts.user` set to \"\" or a whitespace-only string; constructing Options programmatically and forgetting to fill the user/collection-URL field; a frontend binding that sends an empty string instead of None.","commonSituations":"A desktop (Tauri) UI invokes the command before the user typed the profile; a script builds Options with Default::default() and never sets user; form input is trimmed and becomes empty.","solutions":["Set opts.user to the @username or the collection URL before calling run.","In the UI, disable the download action until the profile/URL field is non-empty after trimming.","If the field is optional by design, resolve a default user upstream instead of passing an empty string."],"exampleFix":"// before\nlet opts = Options { dest: \"/tmp/out\".into(), ..Default::default() };\n// after\nlet opts = Options { user: \"@someuser\".into(), dest: \"/tmp/out\".into(), ..Default::default() };","handlingStrategy":"validation","validationCode":"// Rust caller\nif opts.user.trim().is_empty() {\n    return Err(anyhow!(\"informe o perfil (@usuario) ou a URL da coleção antes de iniciar\"));\n}","typeGuard":"fn has_user(opts: &Options) -> bool { !opts.user.trim().is_empty() }","tryCatchPattern":null,"preventionTips":["Disable the download button until the profile/URL input is non-empty after trim.","Never build Options via Default::default() for real runs without setting user.","Trim user input at the UI boundary before storing it in Options."],"tags":["validation","missing-argument","tiktok"],"backgroundTag":"empty-required-field","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}