apache/pulsar · error · RestException

Sink config is not provided

Error message

Sink config is not provided

What it means

registerSink in SinksImpl throws a 400 Bad Request RestException when the submitted SinkConfig is null. The config carries the sink's runtime settings (className, inputs, topics pattern, etc.) needed to derive FunctionDetails. This is the last of the four mandatory-parameter checks, after tenant, namespace and sinkName.

Source

Thrown at pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SinksImpl.java:100

                             final String sinkPkgUrl,
                             final SinkConfig sinkConfig,
                             final AuthenticationParameters authParams) {

        if (!isWorkerServiceAvailable()) {
            throwUnavailableException();
        }

        if (tenant == null) {
            throw new RestException(Response.Status.BAD_REQUEST, "Tenant is not provided");
        }
        if (namespace == null) {
            throw new RestException(Response.Status.BAD_REQUEST, "Namespace is not provided");
        }
        if (sinkName == null) {
            throw new RestException(Response.Status.BAD_REQUEST, "Sink name is not provided");
        }
        if (sinkConfig == null) {
            throw new RestException(Response.Status.BAD_REQUEST, "Sink config is not provided");
        }

        throwRestExceptionIfUnauthorizedForNamespace(tenant, namespace, sinkName, "register", authParams);

        try {
            // Check tenant exists
            worker().getBrokerAdmin().tenants().getTenantInfo(tenant);

            String qualifiedNamespace = tenant + "/" + namespace;
            List<String> namespaces = worker().getBrokerAdmin().namespaces().getNamespaces(tenant);
            if (namespaces != null && !namespaces.contains(qualifiedNamespace)) {
                String qualifiedNamespaceWithCluster = String.format("%s/%s/%s", tenant,
                        worker().getWorkerConfig().getPulsarFunctionsCluster(), namespace);
                if (namespaces != null && !namespaces.contains(qualifiedNamespaceWithCluster)) {
                    log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", sinkName)

                            .attr("namespace3", namespace).log("/ / Namespace does not exist");
                    throw new RestException(Response.Status.BAD_REQUEST, "Namespace does not exist");

View on GitHub (pinned to 820761864e)

Solutions

  1. Send a complete SinkConfig JSON body with the request and set Content-Type: application/json.
  2. Verify the client-side deserialization: log the parsed SinkConfig before calling; if it is null, the body was empty or malformed.
  3. Construct the SinkConfig explicitly (className, topicToSerdeClassName/inputs) before registration.

Example fix

// before
curl -X PUT .../sinks/my-tenant/my-ns/my-sink   # no body -> sinkConfig == null
// after
curl -X PUT .../sinks/my-tenant/my-ns/my-sink \
  -H "Content-Type: application/json" \
  -d '{"className":"org.example.MySink","inputs":"my-topic"}'
Defensive patterns

Strategy: validation

Validate before calling

if (sinkConfig == null) {
    throw new IllegalArgumentException("sinkConfig must be provided");
}
if (sinkConfig.getClassName() == null || sinkConfig.getClassName().isBlank()) {
    throw new IllegalArgumentException("sinkConfig.className must be set");
}

Type guard

boolean hasValidSinkConfig(SinkConfig cfg) {
    return cfg != null && cfg.getClassName() != null && !cfg.getClassName().isBlank();
}

Try / catch

try {
    sinks.registerSink(tenant, ns, name, cfg, null, null, null, authParams);
} catch (RestException e) {
    if (e.getResponse().getStatus() == 400 && "Sink config is not provided".equals(e.getMessage())) {
        // the request body was missing or not deserialized; fix headers/body
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the sink registration endpoint without a request body, with an empty/unparsable body that deserializes to null SinkConfig, or passing null sinkConfig to SinksImpl.registerSink directly.

Common situations: curl requests missing -d/--data or the Content-Type: application/json header so the body is dropped; client SDKs failing silent JSON deserialization into null; scripts that build the config conditionally and skip it when inputs are empty.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/22d600e56ef9761b. Report an issue: GitHub.