{"record":{"id":"093ce4e77d2b3373","repo":"tonhowtf/omniget","slug":"trecho-o-fim-tem-de-vir-depois-do-come-o","errorCode":null,"errorMessage":"trecho {}: o fim tem de vir depois do começo","messagePattern":"trecho (.+?): o fim tem de vir depois do começo","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/sponsorblock.rs","lineNumber":237,"sourceCode":"            return Err(anyhow!(\n                \"trecho {}: categoria desconhecida ({})\",\n                n,\n                s.category\n            ));\n        }\n        if !ACTION_TYPES.contains(&s.action_type.as_str()) {\n            return Err(anyhow!(\n                \"trecho {}: tipo de ação desconhecido ({})\",\n                n,\n                s.action_type\n            ));\n        }\n        if !s.start.is_finite() || !s.end.is_finite() || s.start < 0.0 || s.end < 0.0 {\n            return Err(anyhow!(\"trecho {}: tempo inválido\", n));\n        }\n        let pontual = s.action_type == \"poi\" || s.action_type == \"full\";\n        if !pontual && s.end <= s.start {\n            return Err(anyhow!(\"trecho {}: o fim tem de vir depois do começo\", n));\n        }\n        if s.action_type == \"chapter\" && s.description.trim().is_empty() {\n            return Err(anyhow!(\"trecho {}: capítulo precisa de um título\", n));\n        }\n        if duration > 0.0 && s.end > duration + 1.0 {\n            return Err(anyhow!(\"trecho {}: passa do fim do vídeo\", n));\n        }\n    }\n    // Sobreposição só importa entre trechos que pulam pedaço.\n    let mut cortes: Vec<&NewSegment> = segments\n        .iter()\n        .filter(|s| s.action_type == \"skip\" || s.action_type == \"mute\")\n        .collect();\n    cortes.sort_by(|a, b| a.start.total_cmp(&b.start));\n    for par in cortes.windows(2) {\n        if par[1].start < par[0].end {\n            return Err(anyhow!(\"dois trechos se sobrepõem em {:.1}s\", par[1].start));\n        }","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/sponsorblock.rs#L219-L255","documentation":"validate_segments rejects a SponsorBlock segment whose end time is not strictly after its start time. For non-point actions (not \"poi\"/\"full\"), a segment represents a time range, so an end <= start would describe an empty or inverted range and is meaningless to the SponsorBlock API. The check runs after the finite/non-negative check, so this is specifically about ordering, not validity of the numbers themselves.","triggerScenarios":"Calling build_payload (via submit) with a NewSegment whose action_type is \"skip\", \"mute\", or \"chapter\" and whose end <= start, e.g. start=10.0, end=10.0 or end=5.0.","commonSituations":"A UI letting users type end times freely; swapped start/end fields when converting from another segment format; zero-length segments used as 'markers' instead of a poi action; off-by-one when computing end from start+length with length=0.","solutions":["Fix the segment data so end > start for range-type segments before calling build_payload.","Use action_type \"poi\" or \"full\" if the segment is a single point in time (only start matters).","Add client-side validation on input fields: end must be greater than start.","Check for swapped start/end values if the segment came from an import or conversion step."],"exampleFix":"// before\nNewSegment { action_type: \"skip\".into(), start: 42.0, end: 42.0, .. }\n// after\nNewSegment { action_type: \"skip\".into(), start: 42.0, end: 57.5, .. }","handlingStrategy":"validation","validationCode":"fn valid_range(s: &NewSegment) -> bool {\n    let pontual = s.action_type == \"poi\" || s.action_type == \"full\";\n    pontual || s.end > s.start\n}","typeGuard":"fn is_valid_segment(s: &NewSegment) -> bool {\n    s.start.is_finite() && s.end.is_finite() && s.start >= 0.0 && (s.end > s.start || matches!(s.action_type.as_str(), \"poi\" | \"full\"))\n}","tryCatchPattern":"match build_payload(&id, &user, dur, &segs) {\n    Err(e) if e.to_string().contains(\"o fim tem de vir depois do começo\") => warn_user_about_segment_times(),\n    other => other?,\n}","preventionTips":["Validate end > start in the UI input handler before creating a NewSegment.","Use poi/full action types for point events instead of zero-length ranges.","Check for swapped start/end when importing from other formats."],"tags":["validation","sponsorblock","rust","time-range"],"backgroundTag":"invalid-argument-value","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"}