{"record":{"id":"0111a0ea98258461","repo":"apache/pulsar","slug":"e-getmessage-0111a0","errorCode":null,"errorMessage":"e.getMessage()","messagePattern":"e\\.getMessage\\(\\)","errorType":"http","errorClass":"RestException","httpStatus":400,"severity":"error","filePath":"pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java","lineNumber":317,"sourceCode":"        if (!InstanceUtils.calculateSubjectType(existingComponent.getFunctionDetails()).equals(componentType)) {\n            log.error().attr(\"tenant\", tenant).attr(\"namespace\", namespace).attr(\"componentName\", functionName)\n\n                    .attr(\"componentType\", ComponentTypeUtils.toString(componentType)).log(\"/ / is not a\");\n            throw new RestException(Response.Status.NOT_FOUND, String.format(\"%s %s doesn't exist\",\n                    ComponentTypeUtils.toString(componentType), functionName));\n        }\n\n        FunctionConfig existingFunctionConfig = FunctionConfigUtils\n                .convertFromDetails(existingComponent.getFunctionDetails());\n        // The rest end points take precedence over whatever is there in function config\n        functionConfig.setTenant(tenant);\n        functionConfig.setNamespace(namespace);\n        functionConfig.setName(functionName);\n        FunctionConfig mergedConfig;\n        try {\n            mergedConfig = FunctionConfigUtils.validateUpdate(existingFunctionConfig, functionConfig);\n        } catch (Exception e) {\n            throw new RestException(Response.Status.BAD_REQUEST, e.getMessage());\n        }\n\n        if (existingFunctionConfig.equals(mergedConfig) && isBlank(functionPkgUrl) && uploadedInputStream == null\n                && (updateOptions == null || !updateOptions.isUpdateAuthData())) {\n            log.error().attr(\"tenant\", tenant).attr(\"namespace\", namespace).attr(\"componentName\", functionName)\n\n                    .log(\"/ / Update contains no changes\");\n            throw new RestException(Response.Status.BAD_REQUEST, \"Update contains no change\");\n        }\n\n        FunctionDetails functionDetails;\n        File componentPackageFile = null;\n        try {\n\n            // validate parameters\n            try {\n                componentPackageFile = getPackageFile(\n                        componentType,","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/FunctionsImpl.java#L299-L335","documentation":"During updateFunction, FunctionConfigUtils.validateUpdate merges and validates the submitted FunctionConfig against the existing one. Any validation exception is converted to HTTP 400 with the raw e.getMessage() text. Common causes include illegal field changes (e.g. changing inputs/className/topics in disallowed ways), invalid values, or version-drift between the submitted config and the stored one.","triggerScenarios":"PUT update where the merged config violates FunctionConfig rules — changing the class name or input topics on an existing function, incompatible runtime changes, invalid parallelism/resources, or a config produced by a newer client containing fields the worker's validator rejects.","commonSituations":"Editing function configs via scripts that mutate the GET result incompatibly; Pulsar version mismatch where newer client config fields fail older worker validation; attempting to change immutable fields instead of delete + recreate.","solutions":["Read the 400 message text — it is the validator's explanation of which field is invalid.","Only mutate allowed fields: GET current config, change settings, PUT the full merged config.","For immutable changes (className, inputs), delete and re-create the function.","Align client and cluster Pulsar versions so config field compatibility matches.","Validate the config offline with FunctionConfigUtilsUpdated/the same validation before submitting."],"exampleFix":"// before\nFunctionConfig cfg = admin.functions().getFunctionConfig(t, ns, fn);\ncfg.setClassName(\"com.newpkg.NewClass\"); // immutable -> validateUpdate throws\nadmin.functions().updateFunction(t, ns, fn, cfg, null);\n// after\nadmin.functions().deleteFunction(t, ns, fn);\nadmin.functions().createFunction(newCfgWithNewClassName(), null);","handlingStrategy":"validation","validationCode":"FunctionConfig current = admin.functions().getFunctionConfig(t, ns, fn);\nFunctionConfig merged = mergeAndSanitize(current, submittedChanges); // only copy allowed mutable fields\ntry {\n    FunctionConfigUtils.validateUpdate(current, merged); // same validation the worker runs\n} catch (Exception e) {\n    throw new IllegalArgumentException(\"Config will be rejected: \" + e.getMessage(), e);\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.functions().updateFunction(t, ns, fn, cfg, null);\n} catch (PulsarAdminException e) {\n    if (e.getStatusCode() == 400) {\n        log.error(\"Update validation failed: {}\", e.getMessage()); // raw validator reason\n        throw new ConfigValidationException(e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Treat className/inputs/runtime as immutable; delete + recreate instead of updating.","Run the same FunctionConfigUtils validation client-side before submitting.","Keep client and cluster Pulsar versions aligned.","Fetch the live config and apply minimal diffs rather than rebuilding configs from scratch."],"tags":["rest-api","pulsar-functions","config-validation","http-400"],"backgroundTag":"config-validation-failed","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"}