{"record":{"id":"20a5414ee0b798c7","repo":"Hmbown/CodeWhale","slug":"unrecognised-character-ch-in-duration-s","errorCode":null,"errorMessage":"unrecognised character {ch:?} in duration {s:?}","messagePattern":"unrecognised character (.+?) in duration (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/metrics.rs","lineNumber":91,"sourceCode":"\n    for ch in s.chars() {\n        match ch {\n            '0'..='9' => num_buf.push(ch),\n            'd' | 'h' | 'm' | 's' => {\n                let n: i64 = num_buf\n                    .parse()\n                    .map_err(|_| anyhow::anyhow!(\"invalid duration component: {num_buf:?}\"))?;\n                num_buf.clear();\n                let factor = match ch {\n                    'd' => 86_400,\n                    'h' => 3_600,\n                    'm' => 60,\n                    's' => 1,\n                    _ => unreachable!(),\n                };\n                total += n * factor;\n            }\n            _ => anyhow::bail!(\"unrecognised character {ch:?} in duration {s:?}\"),\n        }\n    }\n\n    if !num_buf.is_empty() {\n        // Trailing bare number — treat as seconds.\n        let n: i64 = num_buf.parse()?;\n        total += n;\n    }\n\n    if total == 0 {\n        anyhow::bail!(\"duration {s:?} resolved to zero seconds\");\n    }\n\n    Ok(total)\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Rollup data model","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/cli/src/metrics.rs#L73-L109","documentation":"Any character other than ASCII digits and the unit letters d/h/m/s aborts duration parsing: spaces, '.', uppercase letters (parse_since lowercases first, but direct calls do not), and unsupported units like w, y, x. The error echoes the offending character and the full input.","triggerScenarios":"Passing \"1.5h\", \"2 w\", \"1H\" via a direct parse call, \"5min\", \"1h,30m\", or ISO-8601 \"PT2H\" style strings to a --since flag.","commonSituations":"Copy-pasting humantime strings with spaces (\"1h 30m\"); decimal hours; expecting week/year or min/ms units; locale-formatted numbers.","solutions":["Rewrite in the supported grammar: \"1h30m\", \"90m\", \"5400s\" — no spaces or decimals","Convert decimals to smaller units (\"1.5h\" -> \"90m\")","Lowercase the input; uppercase units are not accepted by the raw parser"],"exampleFix":"# before\n codewhale metrics --since \"1h 30m\"   # error: unrecognised character ' '\n\n# after\n codewhale metrics --since 1h30m","handlingStrategy":"validation","validationCode":"// Rust: validate against the accepted grammar before calling the CLI/parser:\nfn valid_duration(s: &str) -> bool {\n    let s = s.trim().to_ascii_lowercase();\n    let s = s.strip_prefix(\"now-\").unwrap_or(&s);\n    !s.is_empty()\n        && s.chars().all(|c| c.is_ascii_digit() || matches!(c, 'd' | 'h' | 'm' | 's'))\n        && s.starts_with(|c: char| c.is_ascii_digit())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Strip spaces, commas, and decimals from duration inputs before passing them","Lowercase inputs in wrappers; the raw parser rejects uppercase units","Only d/h/m/s exist — convert weeks (7d), minutes (m), and everything else upfront"],"tags":["cli","metrics","duration","parsing","rust"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}