fix(session): reject non-integer consolidated offsets

maintainer edit: corrupt session metadata can contain JSON strings, nulls, floats, or booleans. Reset non-integer offsets before range checks so recovery keeps valid messages visible instead of falling back to an empty session.
This commit is contained in:
chengyongru 2026-06-03 10:37:32 +08:00 committed by Xubin Ren
parent 0307ee6b73
commit 13178f3eaa
2 changed files with 48 additions and 3 deletions

View File

@ -101,7 +101,11 @@ class Session:
def __post_init__(self) -> None:
# An out-of-range offset (corrupt metadata) would hide all history; reset it.
if not 0 <= self.last_consolidated <= len(self.messages):
if (
isinstance(self.last_consolidated, bool)
or not isinstance(self.last_consolidated, int)
or not 0 <= self.last_consolidated <= len(self.messages)
):
self.last_consolidated = 0
@staticmethod

View File

@ -1,9 +1,12 @@
"""Reset a corrupt last_consolidated offset instead of hiding history (#4066)."""
from nanobot.session.manager import Session
import json
from pathlib import Path
from nanobot.session.manager import Session, SessionManager
def _session(count: int, last_consolidated: int) -> Session:
def _session(count: int, last_consolidated: object) -> Session:
msgs = [{"role": "user", "content": f"msg{i}"} for i in range(count)]
return Session(key="chan:chat", messages=msgs, last_consolidated=last_consolidated)
@ -13,6 +16,44 @@ def test_out_of_range_offset_is_reset():
assert _session(3, -5).last_consolidated == 0
def test_non_integer_offset_is_reset():
for offset in ("999", None, 0.5, True):
assert _session(3, offset).last_consolidated == 0
def test_loaded_corrupt_offset_keeps_messages(tmp_path: Path):
offsets = {
"string": "999",
"null": None,
"float": 0.5,
"bool": True,
}
for name, offset in offsets.items():
manager = SessionManager(tmp_path / name)
path = manager._get_session_path("chan:chat")
path.parent.mkdir(parents=True, exist_ok=True)
message = {"role": "user", "content": f"survived {name}"}
path.write_text(
"\n".join([
json.dumps({
"_type": "metadata",
"key": "chan:chat",
"metadata": {},
"last_consolidated": offset,
}),
json.dumps(message),
]) + "\n",
encoding="utf-8",
)
session = manager.get_or_create("chan:chat")
assert session.messages == [message]
assert session.last_consolidated == 0
assert session.get_history(max_messages=10) == [message]
def test_valid_offset_is_preserved():
session = _session(10, 4)
assert session.last_consolidated == 4