{"record":{"id":"5aa75de388da79aa","repo":"python/cpython","slug":"incomplete-time-component","errorCode":null,"errorMessage":"Incomplete time component","messagePattern":"Incomplete time component","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":406,"sourceCode":"\n        pos += has_sep\n        day = int(dtstr[pos:pos + 2])\n\n        return [year, month, day]\n\n\n_FRACTION_CORRECTION = [100000, 10000, 1000, 100, 10]\n\n\ndef _parse_hh_mm_ss_ff(tstr):\n    # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]\n    len_str = len(tstr)\n\n    time_comps = [0, 0, 0, 0]\n    pos = 0\n    for comp in range(0, 3):\n        if (len_str - pos) < 2:\n            raise ValueError(\"Incomplete time component\")\n\n        time_comps[comp] = int(tstr[pos:pos+2])\n\n        pos += 2\n        next_char = tstr[pos:pos+1]\n\n        if comp == 0:\n            has_sep = next_char == ':'\n\n        if not next_char or comp >= 2:\n            break\n\n        if has_sep and next_char != ':':\n            raise ValueError(\"Invalid time separator: %c\" % next_char)\n\n        pos += has_sep\n\n    if pos < len_str:","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L388-L424","documentation":"The time parser _parse_hh_mm_ss_ff reads exactly two digits per component (HH, MM, SS). If fewer than two characters remain for the component being parsed, it raises ValueError('Incomplete time component') — e.g. a lone trailing '1' after a separator ('12:3' parsed in two-digit steps at the wrong offset, '12:30:5' style truncations depending on separator handling).","triggerScenarios":"datetime.fromisoformat('2021-01-01T12:3'); time strings sliced mid-component; fractional part mistakenly placed without separator so the digit budget shifts ('12305' lengths that leave one dangling digit).","commonSituations":"Truncating timestamps to save space (e.g. s[:15] chopping seconds); regex captures that grabbed a partial component; fixed-width log parsing with off-by-one offsets.","solutions":["Keep time components two-digit: '12:30:05' or '123005'","Truncate at component boundaries, never mid-string: build output from dt.strftime('%H:%M') instead of slicing","For sub-minute precision use strftime('%H:%M:%S') or isoformat(timespec=...)"],"exampleFix":"// before\ns = raw[:16]  # chops '05' to '5'\ndt = datetime.fromisoformat(s)  # ValueError\n\n# after\ndt = datetime.fromisoformat(raw)\nshort = dt.isoformat(timespec='minutes')  # '2021-01-01T12:30'","handlingStrategy":"validation","validationCode":"import re\n_TIME_RE = re.compile(r'^\\d{2}(:?\\d{2}(:?\\d{2}([.,]\\d{1,6})?)?)?$')\ndef parse_time_part(t: str):\n    if not _TIME_RE.fullmatch(t):\n        raise ValueError(f'bad time component layout: {t!r}')\n    return t","typeGuard":"def time_components_complete(t: str) -> bool:\n    body = re.split(r'[.,]', t)[0]\n    parts = re.split(r':?', body)\n    return all(len(p) == 2 for p in parts) and 1 <= len(parts) <= 3","tryCatchPattern":null,"preventionTips":["Always two-digit-pad HH, MM, SS","Truncate timestamps only via isoformat(timespec=...), never slicing","Test parsers against minimum inputs ('T00', 'T0', empty)"],"tags":["datetime","fromisoformat","time-parsing","valueerror","stdlib"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}