Skip to content

@powersync/common: clarify or preserve SyncStreamStatus.subscription.lastSyncedAt precision #945

@whygee-dev

Description

@whygee-dev

@powersync/common: clarify or preserve SyncStreamStatus.subscription.lastSyncedAt precision

Package

  • @powersync/common@1.52.0 (latest on npm as of 2026-05-05)

Disclosure

This report is distilled from behavior reproduced in a staged production environment. Product-specific data, names, and identifiers were redacted and the public repro/draft was edited with AI assistance. The repro and issue text were reviewed and validated by a human before submission.

Summary

SyncStatus.forStream(...).subscription.lastSyncedAt is exposed as a JavaScript Date, but the value appears to be derived from a whole-second last_synced_at core timestamp:

lastSyncedAt: core.last_synced_at != null ? new Date(core.last_synced_at * 1000) : null

That means subsecond precision from caller-side cutoff timestamps is lost. This may be intended, but I could not find documentation for the precision contract.

Minimal reproduction

git clone https://github.com/whygee-dev/powersync-common-sync-stream-last-synced-at-precision-repro.git
cd powersync-common-sync-stream-last-synced-at-precision-repro
npm install
npm test

Observed output:

expected syncedAfter: 2026-04-19T10:00:00.750Z
core last_synced_at: 1776592800
reported lastSyncedAt: 2026-04-19T10:00:00.000Z
precision loss ms: 750
reproduced: stream subscription lastSyncedAt reports whole-second precision

The repro constructs a SyncStatus with an internal stream subscription whose last_synced_at is an epoch-second value, then reads it back through status.forStream(...).subscription.lastSyncedAt.

Expected behavior

One of these should be true:

  • stream status timestamps preserve subsecond precision when exposed as JavaScript Date values; or
  • the API documentation states that stream lastSyncedAt has whole-second precision, so callers know to floor/round cutoff timestamps or compare with tolerance.

Actual behavior

The public value is a Date, but it can be rounded down to the beginning of the second. Code that waits for a stream to sync after a JavaScript timestamp such as 2026-04-19T10:00:00.750Z can see:

subscription.hasSynced === true
subscription.lastSyncedAt === 2026-04-19T10:00:00.000Z

and keep waiting because lastSyncedAt < syncedAfter.

Impact

Applications that subscribe to streams and then wait for subscription.hasSynced plus subscription.lastSyncedAt >= syncedAfter can get false negatives whenever syncedAfter contains milliseconds.

The downstream workaround is to floor the cutoff to whole seconds before comparing. The unclear part is whether that workaround is required by design or compensating for a precision bug.

Question

Is whole-second precision for sync stream lastSyncedAt intended? If yes, can this be documented on the stream status/subscription APIs? If not, can the core status preserve subsecond precision?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions