Why a WordPress Timeout Must Not Trigger a Second Publication

A network timeout on a WordPress create can return an inconclusive response. Retrying the create risks a duplicate publish. Here is how to keep it safe.

The seam where duplicates are born

Automated content pipelines look simple from the outside: a scheduler triggers, the script posts, the post appears. The interesting failures all live in the seam between the script and the publishing endpoint, where a network call can succeed silently, fail loudly, or do neither.

When a small automated pipeline posts to WordPress over REST, the create call may time out. The publishing script then sees no response, no post ID, and no confirmation. Retrying POST /wp/v2/posts after a lost response can create a second post if the first request succeeded.

The WordPress REST API reference exposes POST /wp/v2/posts to create a post and POST /wp/v2/posts/<id> to update a known post. The reference returns id, slug, status, date, and date_gmt, with date in site timezone and date_gmt in GMT. The API does not document a general idempotency-key parameter for creation, and matching slugs do not by themselves guarantee exactly-once publishing.

What "safe retry" actually requires

The Microsoft retry pattern guidance treats retries as appropriate only for transient failures, and warns that operations should be idempotent before retries are layered on. A retry strategy that fans out from multiple layers at once tends to multiply rather than absorb failures.

The AWS Builders’ Library on idempotent APIs goes further: a client request identifier can carry the same intent across retries, but identical parameters alone do not distinguish a retry from a new request. The server must associate the identifier with the effect. The cited WordPress posts reference does not promise this request-identifier contract; a custom plugin would need its own verified guarantees.

For a publication pipeline, that means the safety has to be designed in the pipeline, not borrowed from the API.

Separate draft creation from publication

The concrete design is to keep two steps apart: build the content first, then publish by updating a known post.

Persist an operation record locally before any write. The record holds the intended slot time, the content hash, the agent identity, and an initially empty remote_id. Then create the post as a draft to obtain the numeric ID. Only after the ID is confirmed should the script move to publication, using POST /wp/v2/posts/<id> with status set to publish.

Reviews should bind to the exact visible content, not to a slot time alone. A scheduled slot owned by one writer removes the race where two agents each think they hold it.

Failure walkthrough: a 07:30 MYT scheduled slot

07:30 MYT (Malaysia Time, UTC+8) is 23:30 UTC the previous day; 14:30 MYT is 06:30 UTC the same day. The following timeline is an illustrative design example, not a reported incident.

Before the 07:30 MYT publication slot, the pipeline records a draft-creation operation and calls POST /wp/v2/posts with status=draft. The HTTP request times out. The local record still has remote_id = null.

Now inspect the remote state instead of creating again. Compare the intended slug, author and exact content against candidate drafts using an authenticated, fully paginated read. A verified unique match can establish the numeric ID for subsequent review. An empty or ambiguous lookup does not prove that the earlier request failed: processing may still be in flight, and matching rules can be incomplete. Keep the operation and its publication slot held for operator reconciliation. A client-side request identifier has no deduplication effect unless the server explicitly implements that contract.

In this conservative design, the pipeline does not automatically issue a second POST /wp/v2/posts for the same content, and at no point does it attempt to publish by re-creating.

Verification checklist

  • Operation record is written before the first network call.
  • Local remote_id is only filled from a confirmed create response or a verified re-read.
  • Publication uses POST /wp/v2/posts/<id>, never a second create.
  • Slot owner is recorded; concurrent agents for the same slot are rejected.
  • Retry budget is bounded and coordinated across layers.
  • A slot whose state cannot be inspected stays held for an operator.

When you cannot prove safety

A local ledger is not an atomic transaction with WordPress and does not guarantee exactly-once publication. If the create response is lost and the re-read also fails, do not assume the post is absent. Hold the slot and surface it for manual reconciliation rather than letting a second create risk a duplicate. Fail closed when inspection is unavailable.

Treat publication as an effect that once begun must not be repeated without proof it did not begin. The proof has to come from the remote state, not from a second create call.

For architectural background, see the earlier multi-agent content pipeline walkthrough. The retry and reconciliation rules above are a separate design proposal, not a claim about how that earlier implementation behaves.

Law Wen Feng

Cloud and AI Architect in Malaysia. Writes about Azure infrastructure, AI agents, cloud migration, disaster recovery and Microsoft Fabric. More about Law Tech Rants