fix(export): advance the raw pagination boundary on every event
Exporting a date range from an ENCRYPTED room over-paginated and mislabeled "truncated": oldestRawTs (the how-far-back-have-we-paged boundary) was updated only after the RoomMessage + decryption-failure filters, so undecryptable or non-message old events never advanced it, the fromTs break never fired, and the loop ran to MAX_EXPORT_PAGES. getTs() is unencrypted envelope metadata, so the boundary update now runs for every event, above the filters. Guarded with `ts > 0` so a bogus 0/negative origin_server_ts can't collapse the boundary and cause the opposite failure — a silent early break / under-paginated export (per review, silent omission in an export is worse than the loud over-pagination this fixes). oldestTs (oldest collected in-range message) is unchanged. Two review agents (both confirmed getTs is decryption-independent, no intra-page collection regression, oldestRawTs feeds only the fromTs break, no plaintext regression); the second surfaced the 0-ts under-pagination edge, hence the guard. Not unit-testable (embedded component + needs an E2EE room with undecryptable history). Gate-green (tsc, eslint, prettier, 925 tests, build). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,16 @@ export function ExportRoomHistory({ requestClose }: ExportRoomHistoryProps) {
|
||||
const evId = ev.getId();
|
||||
if (!evId || seen.has(evId)) continue;
|
||||
seen.add(evId);
|
||||
// Advance the raw pagination boundary for EVERY event (any type,
|
||||
// decrypted or not) — getTs() is unencrypted metadata. Gating this on
|
||||
// a decrypted m.room.message let undecryptable/non-message old events
|
||||
// stall oldestRawTs, so the fromTs break never fired → over-paginate
|
||||
// and a false "truncated".
|
||||
const ts = ev.getTs();
|
||||
// Require a positive ts: an event with a bogus 0/negative
|
||||
// origin_server_ts must not collapse the boundary and trigger an early
|
||||
// break (silent under-pagination in the export).
|
||||
if (ts > 0 && ts < oldestRawTs) oldestRawTs = ts;
|
||||
// Attempt decryption for events that haven't been decrypted yet
|
||||
// (paginateEventTimeline may fetch events before the SDK decrypts them)
|
||||
if (ev.isEncrypted() && !ev.getClearContent()) {
|
||||
@@ -100,8 +110,6 @@ export function ExportRoomHistory({ requestClose }: ExportRoomHistoryProps) {
|
||||
}
|
||||
if (ev.getType() !== EventType.RoomMessage) continue;
|
||||
if (ev.isDecryptionFailure()) continue;
|
||||
const ts = ev.getTs();
|
||||
if (ts < oldestRawTs) oldestRawTs = ts;
|
||||
if (fromTs !== null && ts < fromTs) continue;
|
||||
if (toTs !== null && ts > toTs) continue;
|
||||
const content = ev.getContent();
|
||||
|
||||
Reference in New Issue
Block a user