apache/pulsar · error · IllegalArgumentException

Invalid state value

Error message

Invalid state value

What it means

putFunctionState accepts exactly one of byteValue, stringValue, or numberValue in the FunctionState body. When byteValue is null/empty and neither stringValue nor numberValue is set, the worker throws IllegalArgumentException("Invalid state value"), which the surrounding catch converts to a 500 RestException. The request carried no usable value payload.

Source

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

        FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager();
        if (!functionMetaDataManager.containsFunction(tenant, namespace, functionName)) {
            log.warn().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", functionName)

                    .log("putFunctionState does not exist @ / / /");
            throw new RestException(Status.NOT_FOUND, String.format("'%s' is not found", functionName));
        }

        try {
            DefaultStateStore store = worker().getStateStoreProvider().getStateStore(tenant, namespace, functionName);
            ByteBuffer data;
            if (state.getByteValue() == null || state.getByteValue().length == 0) {
                if (state.getStringValue() != null) {
                    data = ByteBuffer.wrap(state.getStringValue().getBytes(UTF_8));
                }  else if (state.getNumberValue() != null) {
                    data = ByteBuffer.allocate(Long.BYTES);
                    data.putLong(state.getNumberValue());
                } else {
                    throw new IllegalArgumentException("Invalid state value");
                }
            } else {
                data = ByteBuffer.wrap(state.getByteValue());
            }
            store.put(key, data);
        } catch (Throwable e) {
            log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", functionName)

                    .attr("key", key).exception(e).log("Error while putFunctionState request @ / / / /");
            throw new RestException(Status.INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

    @Override
    public void uploadFunction(final InputStream uploadedInputStream, final String path,
                               AuthenticationParameters authParams) {

        if (!isWorkerServiceAvailable()) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Include exactly one value field in the body: stringValue (string), numberValue (long), or byteValue (base64 bytes).
  2. Check field names — the server expects stringValue/numberValue/byteValue, not generic "value".
  3. Ensure byteValue base64 decodes to a non-empty byte array if you intend a byte write.
  4. Expect a 500 here despite it being a client payload problem; fix the payload rather than retrying.

Example fix

// before: no value in body -> 500 'Invalid state value'
curl -X PUT .../state/counter -d '{"key":"counter"}'

// after
curl -X PUT .../state/counter -d '{"key":"counter","stringValue":"42"}'
Defensive patterns

Strategy: validation

Validate before calling

// Exactly one value variant must be set
function validStateBody(state) {
  const set = [!!state.stringValue, state.numberValue != null,
               Array.isArray(state.byteValue) && state.byteValue.length > 0].filter(Boolean).length;
  return set === 1 && typeof state.key === 'string' && state.key.length > 0;
}

Type guard

function hasValue(state) {
  return state != null && (
    (typeof state.stringValue === 'string') ||
    (typeof state.numberValue === 'number') ||
    (Array.isArray(state.byteValue) && state.byteValue.length > 0));
}

Try / catch

try {
  await putState(tenant, ns, fn, key, body);
} catch (e) {
  if (e.status === 500 && e.message.includes("Invalid state value")) {
    // payload problem despite 500: add stringValue/numberValue/byteValue and retry
  } else throw e;
}

Prevention

When it happens

Trigger: PUT .../functions/{tenant}/{namespace}/{functionName}/state/{key} with a body like {"key":"k"} — byteValue null/empty, stringValue null, numberValue null. Note the 500 status (not 400) because the IllegalArgumentException is caught by the generic Throwable handler.

Common situations: Clients serializing FunctionState objects with all value fields unset; JSON field name mismatches (e.g. sending "value" instead of "stringValue"); base64 byteValue decoded to an empty array on the server; omitting the value when only intending to update a key.

Related errors


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