Skip to content

fix: report out-of-range SSE timestamps as unparseable, not as a crash - #6

Open
JingHao-Leon wants to merge 2 commits into
dannyota:masterfrom
JingHao-Leon:fix/vmlab-sse-out-of-range-timestamps
Open

JingHao-Leon wants to merge 2 commits into
dannyota:masterfrom
JingHao-Leon:fix/vmlab-sse-out-of-range-timestamps

Conversation

@JingHao-Leon

@JingHao-Leon JingHao-Leon commented Oct 19, 2026 •

Copy link
Copy Markdown

Summary

parse_timestamp() in the SSE lab tooling promised to return None for
anything that is not a usable timestamp, but only its epoch branch actually
guarded against ValueError. The ISO, US and generalized branches fed a
shape-matching value straight into datetime.strptime() / timezone(), so a
value that looks like a timestamp but is not a real instant (2026-02-30,
+9999, a 13:00:00 PM hour) raised out of the function.

The exception escaped scan_package()'s error wrapping and
prepare-sse-data.py's except PreparationError, so the tool died with a
raw ValueError traceback instead of the one-line error report it already
produces for every other unusable value.

parse_timestamp() now converts ValueError into the None it already
promised, so the existing unsupported temporal values /
cannot parse embedded timestamp paths handle these values.

Fixes #5

Before / after

Reproducer: an SSE sample lookup whose whenCreated cell holds
2026-02-30T10:00:00.

$ python installers/vmlab/prepare-sse-data.py \
    --package sse.tgz --output-dir prepared --index sse_lab \
    --anchor 2026-08-15T12:00:00Z --dry-run

Before:

Traceback (most recent call last):
  ...
  File "installers/vmlab/prepare-sse-data.py", line 76, in <module>
    raise SystemExit(main())
  ...
  File "installers/vmlab/sse_data.py", line 245, in scan_package
    return _scan_archive(archive, _package_sha256(package))
  ...
  File "installers/vmlab/sse_timestamps.py", line 109, in parse_timestamp
    naive = datetime.strptime(
  ...
ValueError: day is out of range for month

After:

prepare-sse-data: unsupported temporal values:
sample_text.csv row 2 field whenCreated: '2026-02-30T10:00:00'

Exit code 1 in both cases — the difference is a one-line report versus a
traceback.

Root cause

installers/vmlab/sse_timestamps.py:88 — parse_timestamp only wrapped the
epoch branch in try/except (OverflowError, ValueError). The remaining
branches could raise:

  • installers/vmlab/sse_timestamps.py:109 — datetime.strptime(..., "%Y-%m-%d %H:%M:%S")
  • installers/vmlab/sse_timestamps.py:113 — _fixed_timezone() → timezone(), which rejects an offset outside ±24h
  • installers/vmlab/sse_timestamps.py:128 — datetime.strptime(..., "%m/%d/%Y %I:%M:%S %p")
  • installers/vmlab/sse_timestamps.py:140 — datetime.strptime(..., "%Y%m%d%H%M%S")

The regexes feeding those calls only constrain a value's shape, so
2026-02-30 and +9999 match and leave the range check to strptime /
timezone.

The raise then passed through the two places meant to contain it:
installers/vmlab/sse_data.py:246 (only converts tarfile.TarError,
UnicodeError, csv.Error and OSError) and
installers/vmlab/prepare-sse-data.py:54 (only handles PreparationError).

Verification

Run locally on Python 3.13.9:

  • pytest tests/vmlab/test_sse_data.py — 12 new cases fail before the fix
    (ValueError: day is out of range for month) and pass after it
  • pytest — 1840 passed, 13 failed; the 13 failures are the same
    pre-existing tests/vmlab/test_script_config.py,
    tests/vmlab/test_sse_import_script.py and tests/test_check_lengths.py
    cases that fail on an unmodified checkout too. They shell out to
    /usr/bin/bash, which does not exist on macOS (it is /bin/bash there) —
    that is why they cannot be verified locally, and they are unrelated to
    this change.
  • ruff check .
  • ruff format --check .
  • mypy splunkctl/ — unchanged from the base commit (3 pre-existing errors
    in splunkctl/mcp/dynamic_server.py against the installed MCP SDK)
  • mypy installers/vmlab/sse_timestamps.py installers/vmlab/sse_data.py
  • bash scripts/check-lengths.sh — both changed files are within the
    500-line budget

Changelog: not touched — this repository has no per-PR changelog fragment
mechanism; CHANGELOG.md is written at release time.

CI

The CI workflow is registered for this head SHA but is held at
action_required — the fork pull-request approval gate — so none of its
six jobs (lint, typecheck, test, package, semgrep, docs) have
run
. There are no job results for this commit, and gh pr checks
therefore reports no checks for the branch. The verification listed above
was run locally.

This change was prepared with the assistance of an AI coding agent.

parse_timestamp() only guards the epoch branch against ValueError. A
value that matches the ISO / US / generalized shape but is impossible
to represent (2026-02-30T10:00:00, an out-of-range UTC offset, a
13:00:00 PM clock hour) reaches datetime.strptime()/timezone() and
raises out of the function.

The value then escapes scan_package()'s error wrapping, so
prepare-sse-data.py dies with a raw ValueError traceback instead of the
PreparationError the tool already uses for every other bad value.
parse_timestamp() documented itself as returning None for "other data",
but only the epoch branch actually guarded against ValueError. Every
other branch fed a shape-matching value straight into
datetime.strptime() or timezone(), so an impossible value (2026-02-30,
a 13 o'clock PM hour, a +9999 UTC offset) escaped the function.

The exception then escaped scan_package()'s error wrapping, and
prepare-sse-data.py died with a raw ValueError traceback instead of the
PreparationError it already raises for every other unusable value.

parse_timestamp() now converts ValueError into the None it already
promised, so the existing "unsupported temporal values" /
"cannot parse embedded timestamp" paths handle these values.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

prepare-sse-data.py crashes with a raw ValueError on out-of-range timestamps

1 participant