{"record":{"id":"d130112c7dc3a811","repo":"swc-project/swc","slug":"expected-s-or-ms-units","errorCode":null,"errorMessage":"Expected 's' or 'ms' units","messagePattern":"Expected 's' or 'ms' units","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/swc_css_parser/src/parser/values_and_units/mod.rs","lineNumber":2122,"sourceCode":"\n        if !is!(self, Dimension) {\n            return Err(Error::new(span, ErrorKind::Expected(\"dimension token\")));\n        }\n\n        match bump!(self) {\n            Token::Dimension {\n                dimension: dimension_token,\n            } => {\n                let DimensionToken {\n                    value,\n                    unit,\n                    raw_value,\n                    raw_unit,\n                    ..\n                } = *dimension_token;\n\n                if !is_time_unit(&unit) {\n                    return Err(Error::new(span, ErrorKind::Expected(\"'s' or 'ms' units\")));\n                }\n\n                let unit_len = raw_unit.len() as u32;\n\n                Ok(Time {\n                    span,\n                    value: Number {\n                        span: Span::new(span.lo, span.hi - BytePos(unit_len)),\n                        value,\n                        raw: Some(raw_value),\n                    },\n                    unit: Ident {\n                        span: Span::new(span.hi - BytePos(unit_len), span.hi),\n                        value: unit,\n                        raw: Some(raw_unit),\n                    },\n                })\n            }","sourceCodeStart":2104,"sourceCodeEnd":2140,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_css_parser/src/parser/values_and_units/mod.rs#L2104-L2140","documentation":"Thrown by Parse<Time> in swc_css_parser when the token is a dimension but its unit is not a time unit. After bumping Token::Dimension it checks is_time_unit(&unit), which accepts only s and ms (ASCII case-insensitive). Units like sec, seconds, or misapplied units (px, hz) trigger this error with the span of the entire dimension token.","triggerScenarios":"Values like 'transition-duration: 1sec', 'animation: 500millis', 'duration: 30hz', or unit strings built from user input ('1' + 'econds'). Any dimension whose alphabetic tail is not exactly s or ms fails.","commonSituations":"Authors writing 'sec'/'seconds' out of habit, unit-configuration maps keyed by display names ('second') fed verbatim into generated CSS, and confusion between frequency and time units in audio/animation tooling.","solutions":["Replace non-standard units: '1sec' -> '1s', '1500millis' -> '1500ms' or '1.5s'.","Map friendly unit names to s/ms before generating CSS (second->s, millisecond->ms).","Pre-validate with a whitelist of exactly s and ms."],"exampleFix":"/* before */\na { animation-duration: 2sec; }\n\n/* after */\na { animation-duration: 2s; }","handlingStrategy":"validation","validationCode":"const TIME_UNITS = new Set([\"s\", \"ms\"]);\nfunction hasValidTimeUnit(v: string): boolean {\n  const i = [...v].findIndex(c => /[a-zA-Z]/.test(c));\n  return i > 0 && TIME_UNITS.has(v.slice(i).toLowerCase());\n}","typeGuard":"fn valid_time_unit(unit: &str) -> bool {\n    matches!(unit.to_ascii_lowercase().as_str(), \"s\" | \"ms\")\n}","tryCatchPattern":"use swc_css_parser::error::ErrorKind;\nif let Err(e) = parse_input::<Stylesheet>(src, opts) {\n    if matches!(e.kind(), ErrorKind::Expected(\"'s' or 'ms' units\")) {\n        return Err(format!(\"bad time unit at {:?}\", e.span()).into());\n    }\n    return Err(e.into());\n}","preventionTips":["Normalize friendly unit names (second/seconds/millis) to s/ms before emitting CSS.","Keep a single unit constant for durations instead of accepting free-form strings.","Unit-test the normalizer with common typos ('sec', 'second', 'MS')."],"tags":["css","swc","parser","time","units"],"backgroundTag":"css-invalid-time-unit","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}