halo-dev/halo · error · IllegalStateException
Publishing wait timeout.
Error message
Publishing wait timeout.
What it means
Thrown as an IllegalStateException (HTTP 500) by SinglePageEndpoint.publishSinglePage during a synchronous publish. After setting spec.releaseSnapshot and updating, the endpoint re-fetches the page and waits for the reconciler to populate the LAST_RELEASED_SNAPSHOT annotation to match the requested release snapshot. It retries 10 times at 100ms intervals; if the annotation still mismatches, doOnError rethrows 'Publishing wait timeout.'
Source
Thrown at application/src/main/java/run/halo/app/core/endpoint/console/SinglePageEndpoint.java:348
return Mono.just(post);
}
return client.fetch(SinglePage.class, name)
.flatMap(latest -> {
var latestReleasedSnapshotName =
MetadataUtil.nullSafeAnnotations(latest).get(Post.LAST_RELEASED_SNAPSHOT_ANNO);
if (!StringUtils.equals(
latestReleasedSnapshotName,
latest.getSpec().getReleaseSnapshot())) {
return Mono.error(new IllegalStateException(
"SinglePage publishing status is not as expected"));
}
return Mono.just(latest);
})
.retryWhen(Retry.fixedDelay(10, Duration.ofMillis(100))
.filter(IllegalStateException.class::isInstance))
.doOnError(IllegalStateException.class, err -> {
log.error("Failed to publish single page [{}]", name, err);
throw new IllegalStateException("Publishing wait timeout.");
});
})
.flatMap(page -> ServerResponse.ok().bodyValue(page));
}
Mono<ServerResponse> listSinglePage(ServerRequest request) {
var listRequest = new SinglePageQuery(request);
return singlePageService
.list(listRequest)
.flatMap(listedPages -> ServerResponse.ok().bodyValue(listedPages));
}
}
View on GitHub (pinned to d2f5165f9c)
Solutions
- Retry the publish request after a few seconds once the reconciler has had time to run.
- Publish with ?async=true to skip the wait-for-reconcile step and return immediately after the spec update.
- Inspect server logs for the logged 'Failed to publish single page [{name}]' and any reconciler errors, then fix the underlying reconcile failure.
- Check extension reconcile health and DB latency; restart the pod if the reconciler is wedged.
Example fix
// before: synchronous publish that times out
// POST /apis/.../singlepages/{name}/publish
// after: fire-and-forget async publish
// POST /apis/.../singlepages/{name}/publish?async=true Defensive patterns
Strategy: retry
Validate before calling
// prefer async publish to avoid the synchronous reconcile wait boolean async = true; // set ?async=true on the publish request
Try / catch
// reactive: retry publish a couple of times with backoff, fall back to async
client.postPublish(name)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
.filter(t -> t instanceof IllegalStateException))
.onErrorResume(IllegalStateException.class,
e -> client.postPublishAsync(name)); Prevention
- Use ?async=true for publish when you do not need the reconciled result synchronously.
- Monitor reconciler health and DB latency so the ~1s budget is comfortably met under load.
When it happens
Trigger: POST /apis/api.console.halo.run/v1alpha1/singlepages/{name}/publish WITHOUT ?async=true when the SinglePage reconciler is slow, stalled, or erroring so that metadata.annotations['plugin.halo.run/last-released-snapshot'] never catches up to spec.releaseSnapshot within ~1 second.
Common situations: Reconciler thread pool saturated under load; the snapshot/reconciler controller threw and stopped updating the page; database latency pushes reconcile past the 1s budget; a custom plugin hook blocks the reconcile; deploying on a resource-constrained node where reconcile is slow.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Invalid file type, only jar is supported
- validation.error.email.pattern
- Name is required
- Email is required
- validation.error.password.size
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/1dfb28fda51599df.
Report an issue: GitHub.