diff --git a/agentrace/parse.py b/agentrace/parse.py index 162e1ab..d921b0a 100644 --- a/agentrace/parse.py +++ b/agentrace/parse.py @@ -72,6 +72,8 @@ def find_sessions(root: Path | None = None) -> list[Path]: def _ts(value: str | None) -> datetime | None: + if not isinstance(value, str): + return None if not value: return None try: diff --git a/tests/test_agentrace.py b/tests/test_agentrace.py index d013eee..48dca39 100644 --- a/tests/test_agentrace.py +++ b/tests/test_agentrace.py @@ -146,6 +146,36 @@ def test_non_agent_tools_are_ignored(tmp_path): assert parse_session(p).runs == [] +def test_non_string_timestamp_does_not_crash(tmp_path): + """A numeric timestamp (malformed transcript) should not crash _ts.""" + p = tmp_path / "s.jsonl" + p.write_text( + json.dumps({ + "type": "assistant", + "timestamp": 1718000000, + "message": { + "content": [ + {"type": "tool_use", "id": "t1", "name": "Agent", "input": {"prompt": "go"}} + ] + }, + }) + + "\n" + + json.dumps({ + "type": "user", + "timestamp": 1718000600, + "message": { + "content": [ + {"type": "tool_result", "tool_use_id": "t1", "content": "done"} + ] + }, + }) + ) + s = parse_session(p) + assert len(s.runs) == 1 + assert s.runs[0].started_at is None + assert s.runs[0].ended_at is None + + def test_tool_use_without_id_is_skipped(tmp_path): """A tool_use block missing an id should be skipped rather than crashing with KeyError.""" p = tmp_path / "s.jsonl"