{"record":{"id":"a8e418026708a5ee","repo":"apache/pulsar","slug":"kubernetes-secret-should-be-a-map-containing-id-ke","errorCode":null,"errorMessage":"Kubernetes Secret should be a Map containing id/key pairs","messagePattern":"Kubernetes Secret should be a Map containing id/key pairs","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java","lineNumber":138,"sourceCode":"            Type type = new TypeToken<Map<String, Object>>() {\n            }.getType();\n            Map<String, Object> secretsMap = new Gson().fromJson(functionDetails.getSecretsMap(), type);\n\n            for (Object object : secretsMap.values()) {\n                if (object instanceof Map) {\n                    @SuppressWarnings(\"unchecked\") // secret values are expected to be Map<String, String>\n                    Map<String, String> kubernetesSecret = (Map<String, String>) object;\n                    if (kubernetesSecret.size() < 2) {\n                        throw new IllegalArgumentException(\"Kubernetes Secret should contain id and key\");\n                    }\n                    if (!kubernetesSecret.containsKey(idKey)) {\n                        throw new IllegalArgumentException(\"Kubernetes Secret should contain id information\");\n                    }\n                    if (!kubernetesSecret.containsKey(keyKey)) {\n                        throw new IllegalArgumentException(\"Kubernetes Secret should contain key information\");\n                    }\n                } else {\n                    throw new IllegalArgumentException(\"Kubernetes Secret should be a Map containing id/key pairs\");\n                }\n            }\n        }\n    }\n}\n","sourceCodeStart":120,"sourceCodeEnd":144,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java#L120-L144","documentation":"doAdmissionChecks requires every value in the function's secrets map to be a Map of id/key pairs. If any secret value is not a Map (e.g. a String, Number, or List), this IllegalArgumentException is thrown, since Kubernetes secret references cannot be expressed in another shape.","triggerScenarios":"Submitting a function whose secrets config contains a scalar entry, e.g. secrets: {\"mysecret\": \"db-creds\"} or a number/boolean, so the instanceof Map check fails and the else branch throws.","commonSituations":"using the DefaultSecretsProvider/ClearSecretsProvider syntax (plain string secret values) with KubernetesSecretsProviderConfigurator; YAML parsing a value as a scalar because of quoting; migration from another secrets provider without updating secret formats.","solutions":["Convert each secret value to a Map with id and key fields: {\"<secretId>\": {\"id\": \"<k8s-secret-name>\", \"key\": \"<key>\"}}","If you intend plain values, switch the worker's secretsProviderConfigurator to DefaultSecretsProviderConfigurator instead of the Kubernetes one","Unquote/fix YAML so the secret entry parses as a mapping, not a scalar"],"exampleFix":"// before\nsecrets: {\"mysecret\": \"db-creds\"}\n// after\nsecrets: {\"mysecret\": {\"id\": \"db-creds\", \"key\": \"password\"}}","handlingStrategy":"type-guard","validationCode":"void requireSecretsAreMaps(Map<String, Object> secrets) {\n    for (Map.Entry<String, Object> e : secrets.entrySet()) {\n        if (!(e.getValue() instanceof Map)) {\n            throw new IllegalArgumentException(\"Secret '\" + e.getKey() + \"' must be a Map of id/key pairs\");\n        }\n    }\n}","typeGuard":"static boolean isSecretRefMap(Object v) {\n    return v instanceof Map<?, ?> m && m.containsKey(\"id\") && m.containsKey(\"key\");\n}","tryCatchPattern":"try {\n    admin.functions().createFunction(functionConfig);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"should be a Map\")) { /* convert scalar secret values to {id, key} maps */ }\n}","preventionTips":["Convert legacy plain-string secrets when switching to the K8s configurator","Watch YAML quoting so entries parse as maps, not scalars","Centralize secret config generation in one typed helper"],"tags":["kubernetes","secrets","validation","type-mismatch"],"backgroundTag":"secret-config-validation","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"}