mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:20:44 +00:00
fix: tighten catchup balloon filter + suppress unhandled rejection in write queue
Address Codex P1 review feedback:
- Balloon filter: only skip when associatedMessageType OR balloonBundleId
is set alongside associatedMessageGuid. Threaded replies use
threadOriginatorGuid and are unaffected.
- Write queue: .catch(() => {}) on the cleanup promise so a rejected
next doesn't surface as an unhandled rejection in Node 22+.
This commit is contained in:
@@ -498,19 +498,27 @@ async function runBlueBubblesCatchupInner(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip balloon/tapback/reaction events: they carry an
|
||||
// Skip tapback/reaction/balloon events. These carry an
|
||||
// `associatedMessageGuid` pointing at the parent text message and
|
||||
// have a different `guid` of their own. The live webhook path handles
|
||||
// them via the debouncer, which coalesces balloons with their parent.
|
||||
// balloons via the debouncer, which coalesces them with their parent.
|
||||
// Without debouncing here, replaying a balloon would dispatch it as a
|
||||
// standalone message — producing a duplicate reply to the parent.
|
||||
//
|
||||
// Guard: only skip when `associatedMessageType` is set (tapbacks and
|
||||
// reactions — e.g., "like", 2000) OR `balloonBundleId` is set (URL
|
||||
// previews, stickers). iMessage threaded replies use a separate
|
||||
// `threadOriginatorGuid` field and do NOT set either of these, so
|
||||
// they pass through for correct catchup replay.
|
||||
const assocGuid =
|
||||
typeof rec.associatedMessageGuid === "string"
|
||||
? rec.associatedMessageGuid.trim()
|
||||
: typeof rec.associated_message_guid === "string"
|
||||
? rec.associated_message_guid.trim()
|
||||
: "";
|
||||
if (assocGuid) {
|
||||
const assocType = rec.associatedMessageType ?? rec.associated_message_type;
|
||||
const balloonId = typeof rec.balloonBundleId === "string" ? rec.balloonBundleId.trim() : "";
|
||||
if (assocGuid && (assocType != null || balloonId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,11 +170,17 @@ export function createPersistentDedupe(options: PersistentDedupeOptions): Persis
|
||||
const prev = fileWriteQueues.get(filePath) ?? Promise.resolve();
|
||||
const next = prev.then(fn, fn);
|
||||
fileWriteQueues.set(filePath, next);
|
||||
void next.finally(() => {
|
||||
if (fileWriteQueues.get(filePath) === next) {
|
||||
fileWriteQueues.delete(filePath);
|
||||
}
|
||||
});
|
||||
// Cleanup: remove the queue entry once this link settles, but only if
|
||||
// no newer work was chained after us. The `.catch(() => {})` prevents
|
||||
// an unhandled rejection when `next` rejects — callers still observe
|
||||
// the rejection through the returned `next` promise directly.
|
||||
next
|
||||
.finally(() => {
|
||||
if (fileWriteQueues.get(filePath) === next) {
|
||||
fileWriteQueues.delete(filePath);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user