{"record":{"id":"1dfb28fda51599df","repo":"halo-dev/halo","slug":"publishing-wait-timeout","errorCode":null,"errorMessage":"Publishing wait timeout.","messagePattern":"Publishing wait timeout\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":500,"severity":"error","filePath":"application/src/main/java/run/halo/app/core/endpoint/console/SinglePageEndpoint.java","lineNumber":348,"sourceCode":"                        return Mono.just(post);\n                    }\n                    return client.fetch(SinglePage.class, name)\n                            .flatMap(latest -> {\n                                var latestReleasedSnapshotName =\n                                        MetadataUtil.nullSafeAnnotations(latest).get(Post.LAST_RELEASED_SNAPSHOT_ANNO);\n                                if (!StringUtils.equals(\n                                        latestReleasedSnapshotName,\n                                        latest.getSpec().getReleaseSnapshot())) {\n                                    return Mono.error(new IllegalStateException(\n                                            \"SinglePage publishing status is not as expected\"));\n                                }\n                                return Mono.just(latest);\n                            })\n                            .retryWhen(Retry.fixedDelay(10, Duration.ofMillis(100))\n                                    .filter(IllegalStateException.class::isInstance))\n                            .doOnError(IllegalStateException.class, err -> {\n                                log.error(\"Failed to publish single page [{}]\", name, err);\n                                throw new IllegalStateException(\"Publishing wait timeout.\");\n                            });\n                })\n                .flatMap(page -> ServerResponse.ok().bodyValue(page));\n    }\n\n    Mono<ServerResponse> listSinglePage(ServerRequest request) {\n        var listRequest = new SinglePageQuery(request);\n        return singlePageService\n                .list(listRequest)\n                .flatMap(listedPages -> ServerResponse.ok().bodyValue(listedPages));\n    }\n}\n","sourceCodeStart":330,"sourceCodeEnd":361,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/core/endpoint/console/SinglePageEndpoint.java#L330-L361","documentation":"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.'","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: synchronous publish that times out\n//   POST /apis/.../singlepages/{name}/publish\n// after: fire-and-forget async publish\n//   POST /apis/.../singlepages/{name}/publish?async=true","handlingStrategy":"retry","validationCode":"// prefer async publish to avoid the synchronous reconcile wait\nboolean async = true; // set ?async=true on the publish request","typeGuard":null,"tryCatchPattern":"// reactive: retry publish a couple of times with backoff, fall back to async\nclient.postPublish(name)\n    .retryWhen(Retry.backoff(3, Duration.ofSeconds(2))\n        .filter(t -> t instanceof IllegalStateException))\n    .onErrorResume(IllegalStateException.class,\n        e -> client.postPublishAsync(name));","preventionTips":["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."],"tags":["singlepage","publish","reconciler","timeout","webflux"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}