Understanding BlueSky Read-After-Write Consistency

Read-after-write consistency is the gap between a successful write and every read surface reflecting that write. On BlueSky, this matters because a post record can exist on the user's PDS before every AppView response, profile, thread, timeline, or custom feed has caught up.

For normal creators, this may look like a post that seems late after a refresh. For developers, it is a state-management problem: do not confuse delayed visibility with a failed mutation.

Read BlueSky's official read-after-write guide for the AppView, PDS, and Atproto-Repo-Rev behavior behind this issue.

Why a successful write can look delayed

BlueSky's read-after-write documentation explains that services such as the AppView are eventually consistent because they do not have transactional writes from the user's PDS. The PDS can have the record while a derived read route is still serving an older indexed view.

The PDS can smooth over some routes by comparing AppView state with local records, but that behavior is not exhaustive across every possible view, feed, client, custom feed, or third-party service.

The Atproto-Repo-Rev header matters

The AppView communicates indexed repository state with the Atproto-Repo-Rev response header. If the PDS sees a response built from an older repository rev, it can determine whether newer local records should affect that response.

That is useful for user-facing clients, but it does not mean developers should blindly retry writes. A retry can create duplicate posts if the original record was already created successfully.

What client applications should track

The returned AT URI from the write call.

The returned CID or commit reference when available.

The local queue item or scheduler job ID.

The last known publish attempt timestamp.

Whether the latest read failure is a real API error or only a stale AppView response.

If you are also debugging lower-level stream events, pair this guide with the BlueSky firehose sequence and event decoder so sequence gaps and delayed reads are not mixed together.

A safer refresh pattern after posting

After a createRecord or publish action succeeds, the client should mark the local job as published or pending visibility based on returned identifiers. Then it can poll or refresh the relevant view gently, instead of assuming failure after one stale response.

If the UI needs to show confidence, use states such as published, syncing, visible, and failed. Avoid collapsing all of those into a single red error message.

How ONYX handles the user-facing problem

ONYX is designed around deterministic scheduling state. The queue item, scheduled time, account, draft body, and publish attempt stay tied together, so users do not need to manually chase AppView timing after every post.

The practical goal is stable behavior: write once, track the job, avoid duplicate retries, and let the user keep planning the next approved post while the network finishes indexing.

Use ONYX for reviewed BlueSky scheduling when you want posting state tracked outside the feed refresh cycle.