{"record":{"id":"352571ee6b010c77","repo":"python/cpython","slug":"isoformat-time-too-short","errorCode":null,"errorMessage":"Isoformat time too short","messagePattern":"Isoformat time too short","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":449,"sourceCode":"\n            len_remainder = len_str - pos\n\n            if len_remainder >= 6:\n                to_parse = 6\n            else:\n                to_parse = len_remainder\n\n            time_comps[3] = int(tstr[pos:(pos+to_parse)])\n            if to_parse < 6:\n                time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]\n\n    return time_comps\n\ndef _parse_isoformat_time(tstr):\n    # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]\n    len_str = len(tstr)\n    if len_str < 2:\n        raise ValueError(\"Isoformat time too short\")\n\n    # This is equivalent to re.search('[+-Z]', tstr), but faster\n    tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1)\n    timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr\n\n    time_comps = _parse_hh_mm_ss_ff(timestr)\n\n    hour, minute, second, microsecond = time_comps\n    became_next_day = False\n    error_from_components = False\n    error_from_tz = None\n    if (hour == 24):\n        if all(time_comp == 0 for time_comp in time_comps[1:]):\n            hour = 0\n            time_comps[0] = hour\n            became_next_day = True\n        else:\n            error_from_components = True","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L431-L467","documentation":"_parse_isoformat_time requires at least two characters ('HH') before doing anything; a time portion shorter than 2 chars — empty, a lone digit, or a string where the tz search consumed everything — raises ValueError('Isoformat time too short').","triggerScenarios":"datetime.fromisoformat('2021-01-01T') (empty time after separator); '2021-01-01T1'; a string like '12+01:00' passed as a bare time where tz_pos splits leave '<2' chars; fromisoformat on a date-only string via datetime (older versions error differently, some paths land here).","commonSituations":"Splitting 'dateTtime' on 'T' and feeding the empty right half; optional time fields defaulting to '' instead of None; templates emitting the separator when the time part is absent.","solutions":["Omit the 'T' and time entirely for date-only values: use date.fromisoformat or datetime.fromisoformat('2021-01-01')","Guard empty optional parts: parse time only if time_part, else midnight: datetime.combine(d, time())","Validate with len check or regex before calling fromisoformat"],"exampleFix":"// before\ndt = datetime.fromisoformat(f\"{d_str}T{t_str}\")  # t_str == '' -> ValueError\n\n# after\nfrom datetime import datetime, time\ndt = (datetime.fromisoformat(f\"{d_str}T{t_str}\") if t_str\n      else datetime.combine(datetime.fromisoformat(d_str).date(), time()))","handlingStrategy":"validation","validationCode":"def join_iso(d_str: str, t_str: str) -> str:\n    if not t_str:\n        return d_str  # date-only is valid input to datetime.fromisoformat\n    return f'{d_str}T{t_str}'","typeGuard":"def time_part_present(t: str) -> bool:\n    return len(t) >= 2","tryCatchPattern":null,"preventionTips":["Treat empty optional time as absent, not ''","Default missing times to T00:00 explicitly if midnight is intended","Split on 'T' once and validate both halves' lengths"],"tags":["datetime","fromisoformat","time-parsing","valueerror","stdlib"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}