Resumed sessions stamp new messages with stale timestamp from previous last message
- 主要语言
- Rust
- 星标
- 54.2k
- 派生
- 6.2k
- 平均合并
- 3 天 4 小时
- 30 天内合并 PR
- 240
描述
## Summary
When a user reopens an existing chat session after restarting Goose, every new message's integer column gets stamped with the timestamp of the session's last pre-resume message instead of the actual current time. The string column remains correct. Since the UI sorts and filters by the integer column, hours or days of new conversation sort into the past and appear missing from the chat view.
## Root cause
In `crates/goose/src/session/session_manager.rs`, line ~1925:
```rust
let latest: Option =
sqlx::query_scalar("SELECT MAX(created_timestamp) FROM messages WHERE session_id = ?")
.bind(session_id)
.fetch_one(&mut *tx)
.await?;
let created = message.created.max(latest.unwrap_or(message.created));
```
`Message::user()` and `Message::assistant()` both set `created = Utc::now().timestamp()` (current time in seconds). But the persist code takes `.max()` of the new message's timestamp and `MAX(created_timestamp)` from existing messages to ensure messages never sort ahead of what's already stored.
The bug fires when the existing max timestamp is stale or uses a different unit (milliseconds from an older Goose version). If `MAX(created_timestamp)` returns a stale value that is larger than the current time (e.g. a millisecond timestamp from an older version, or a previously corrupted value), `.max()` picks the stale value and every new message inherits it.
## Reproduction
1. Create a session and send several messages
2. Note the last message's `created_timestamp` value
3. Quit Goose
4. Reopen Goose and resume the same session
5. Send new messages
6. Check `created_timestamp` vs `timestamp` in the messages table for the new rows
All new rows carry the same stale value from the session's last pre-resume message, while the string `timestamp` column shows correct per-message times.
## Evidence
- 32 of 319 sessions on the reporting machine exhibit this stale-stamp signature
- The stale value always equals the session's final pre-resume message timestamp
- The behavior recurs on every resume of affected sessions, surviving upgrades from 1.48.0 to 1.50.0
- Users believe data is lost because the UI sorts by the corrupted column, hiding recent messages
## Proposed fix
Normalize the `latest` value to seconds before comparing with `message.created`, using the existing `MILLISECOND_TIMESTAMP_THRESHOLD` constant (10_000_000_000):
```rust
let latest_normalized = latest.map(|t| {
if t > MILLISECOND_TIMESTAMP_THRESHOLD { t / 1000 } else { t }
}).unwrap_or(message.created);
let created = message.created.max(latest_normalized);
```
This ensures the `.max()` comparison operates on the same unit scale, preventing stale or mismatched-unit values from clobbering new timestamps.
贡献指南
调研方向
The bug is in crates/goose/src/session/session_manager.rs around line 1925. Start by reading the session persistence logic and the Message struct. Run the reproduction steps to see the timestamp mismatch. The fix involves normalizing the `latest` timestamp using the `MILLISECOND_TIMESTAMP_THRESHOLD` constant before the max comparison. Done looks like new messages after a resume get correct integer timestamps.
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- sqlite
- 领域
- backend, databases
- Issue 类型
- 缺陷
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 活跃度
- 活跃
- 描述清晰度
- 描述清楚
- 新手友好度
- 75/100