apache/pulsar · error · ParameterException

Sink archive not specified

Error message

Sink archive not specified

What it means

validateSinkConfigs requires sinkConfig.archive to be set before deploying/updating a sink; the archive is the NAR/package URL pointing to the sink implementation. If the archive field is blank after argument processing, a ParameterException is thrown.

Source

Thrown at pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/CmdSinks.java:633

            if (null != runtimeFlags) {
                sinkConfig.setRuntimeFlags(runtimeFlags);
            }

            // check if configs are valid
            validateSinkConfigs(sinkConfig);
        }

        protected Map<String, Object> parseConfigs(String str) throws JsonProcessingException {
            ObjectMapper mapper = ObjectMapperFactory.getMapper().getObjectMapper();
            TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};

            return mapper.readValue(str, typeRef);
        }

        protected void validateSinkConfigs(SinkConfig sinkConfig) {

            if (isBlank(sinkConfig.getArchive())) {
                throw new ParameterException("Sink archive not specified");
            }

            org.apache.pulsar.common.functions.Utils.inferMissingArguments(sinkConfig);

            if (!Utils.isFunctionPackageUrlSupported(sinkConfig.getArchive())
                    && !sinkConfig.getArchive().startsWith(Utils.BUILTIN)) {
                if (!new File(sinkConfig.getArchive()).exists()) {
                    throw new IllegalArgumentException(String.format("Sink Archive file %s does not exist",
                            sinkConfig.getArchive()));
                }
            }
        }

        protected String validateSinkType(String sinkType) throws IOException {
            Set<String> availableSinks;
            try {
                availableSinks = getAdmin().sinks().getBuiltInSinks().stream()
                        .map(ConnectorDefinition::getName).collect(Collectors.toSet());

View on GitHub (pinned to 820761864e)

Solutions

  1. Add --archive /path/to/sink.nar (or a package:// URL)
  2. Or add --sink-type <name> to use a built-in connector
  3. If using --sink-config-file, include the archive field in the JSON

Example fix

// before
pulsar-admin sinks create --tenant t --namespace n --name s --inputs i --sink-config '{...}'
// after
pulsar-admin sinks create --tenant t --namespace n --name s --inputs i --archive ./my-sink.nar
Defensive patterns

Strategy: validation

Validate before calling

if (!args.includes('--archive') && !args.includes('--sink-type')) {
  throw new Error('Sink requires --archive or --sink-type');
}

Type guard

const sinkReady = (c) => Boolean(c && (c.archive || c.sinkType));

Try / catch

try {
  // run sinks create
} catch (e) {
  if (e.message.includes('Sink archive not specified')) { /* add --archive/--sink-type */ }
  throw e;
}

Prevention

When it happens

Trigger: Running `pulsar-admin sinks create` without --archive and without --sink-type (which would otherwise be converted to builtin://...), so the resulting SinkConfig has no archive.

Common situations: Forgetting the archive flag entirely; relying on a config file that omits the archive field; typo'd flag name so the value never reaches the config.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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