{"record":{"id":"acba387cb666f256","repo":"apache/pulsar","slug":"can-t-find-owner-for-topic-s","errorCode":null,"errorMessage":"Can't find owner for topic %s","messagePattern":"Can't find owner for topic (.+?)","errorType":"http","errorClass":"RestException","httpStatus":412,"severity":"warning","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":759,"sourceCode":"                ).thenAccept(pair -> {\n                    LookupResult lookupResult = pair.getLeft();\n                    boolean isTopicOwned = pair.getRight();\n\n                    if (!isTopicOwned) {\n                        boolean newAuthoritative = isLeaderBroker(pulsar());\n                        URI redirect = lookupResult.toRedirectUri(uri.getRequestUri(), newAuthoritative);\n                        // Redirect\n                        log.debug()\n                                .attr(\"redirect\", redirect)\n                                .log(\"Redirecting the rest call\");\n                        throw new WebApplicationException(\n                                Response.temporaryRedirect(redirect).build());\n                    }\n                }).exceptionally(ex -> {\n                    if (ex.getCause() instanceof IllegalArgumentException\n                            || ex.getCause() instanceof IllegalStateException) {\n                            log.debug().attr(\"topic\", topicName).exception(ex).log(\"Failed to find owner for topic\");\n                                                throw new RestException(Status.PRECONDITION_FAILED,\n                                                        \"Can't find owner for topic \"\n                                                        + topicName);\n                    } else if (ex.getCause() instanceof WebApplicationException) {\n                        throw (WebApplicationException) ex.getCause();\n                    } else {\n                        throw new RestException(ex.getCause());\n                    }\n                });\n    }\n\n    /**\n     * If the namespace is global, validate the following - 1. If replicated clusters are configured for this global\n     * namespace 2. If local cluster belonging to this namespace is replicated 3. If replication is enabled for this\n     * namespace <br/>\n     * It validates if local cluster is part of replication-cluster. If local cluster is not part of the replication\n     * cluster then it redirects request to peer-cluster if any of the peer-cluster is part of replication-cluster of\n     * this namespace. If none of the cluster is part of the replication cluster then it fails the validation.\n     *","sourceCodeStart":741,"sourceCodeEnd":777,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L741-L777","documentation":"validateTopicOwnershipAsync wraps the whole lookup+ownership chain in exceptionally: if the failure cause is an IllegalArgumentException or IllegalStateException (e.g. malformed topic name, invalid namespace/bundle state, service unit already being handled), it is converted to a 412 PRECONDITION_FAILED 'Can't find owner for topic <topic>'. It masks the underlying deterministic problem with a generic 'can't find owner' message.","triggerScenarios":"Any topic admin call whose lookup chain throws IllegalArgumentException (bad topic/namespace name format, invalid partition index) or IllegalStateException (invalid state during ownership/redirect) — the exceptionally block at the end of validateTopicOwnershipAsync rethrows as PRECONDITION_FAILED.","commonSituations":"Malformed topic names passed to the REST API (wrong punctuation, empty namespace, bad partition suffix); namespace deleted concurrently with lookup; broker-side state machine issues after failover; old client/REST paths sending legacy topic name formats to a newer broker.","solutions":["Validate the topic's full name format (persistent://tenant/namespace/topic or tenant/namespace/topic) before calling the API","Check broker logs at DEBUG for 'Failed to find owner for topic' with the wrapped exception to see the real cause","Confirm the namespace exists and its bundles are loaded (pulsar-admin namespaces get-bundles)","If caused by transient state during failover, retry once ownership is stable"],"exampleFix":"// before\ncurl .../admin/v2/persistent/my-tenant/ns/topic-\n// after (valid topic name)\ncurl .../admin/v2/persistent/my-tenant/ns/topic-0","handlingStrategy":"try-catch","validationCode":"// Validate topic name format before the API call\nboolean validTopicName(String t) {\n    String noScheme = t.replaceFirst(\"^(persistent|non-persistent)://\", \"\");\n    String[] p = noScheme.split(\"/\");\n    return p.length == 3 && !p[0].isEmpty() && !p[1].isEmpty() && !p[2].isEmpty();\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.topics().getStats(topic);\n} catch (PulsarAdminException.PreconditionFailedException e) {\n    if (e.getMessage().startsWith(\"Can't find owner for topic\")) {\n        // deterministic cause (bad name / invalid state): fix input, do not blind-retry\n        checkBrokerDebugLogsForWrappedCause();\n    } else throw e;\n}","preventionTips":["Normalize/validate topic names (including partition suffix) before REST calls","Check broker DEBUG logs for the wrapped IllegalArgumentException/IllegalStateException cause","Ensure the namespace exists and its bundles are loaded before topic admin operations"],"tags":["http-412","topic","lookup","precondition-failed"],"backgroundTag":"no-owner-for-service-unit","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}