{"record":{"id":"950d509cf0d3601c","repo":"apache/pulsar","slug":"kubernetes-secret-should-contain-id-and-key","errorCode":null,"errorMessage":"Kubernetes Secret should contain id and key","messagePattern":"Kubernetes Secret should contain id and key","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java","lineNumber":129,"sourceCode":"        return new TypeToken<Map<String, String>>() {\n        }.getType();\n    }\n\n    // The secret object should be of type Map<String, String> and it should contain \"id\" and \"key\"\n    @Override\n    public void doAdmissionChecks(AppsV1Api appsV1Api, CoreV1Api coreV1Api, String jobNamespace, String jobName,\n                                  FunctionDetails functionDetails) {\n        if (!StringUtils.isEmpty(functionDetails.getSecretsMap())) {\n            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":111,"sourceCodeEnd":144,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/secrets/src/main/java/org/apache/pulsar/functions/secretsproviderconfigurator/KubernetesSecretsProviderConfigurator.java#L111-L144","documentation":"During function admission (doAdmissionChecks) each exposed secret must be a Map describing a Kubernetes secret with both an 'id' (secret name) and 'key' (key within the secret). If a secret entry's Map has fewer than 2 entries, the check throws this IllegalArgumentException because neither a valid id/key pair nor a well-formed secret reference can be present.","triggerScenarios":"Calling doAdmissionChecks (via function submission/validation, e.g. testConfigValidation) with a function config whose secrets map contains an entry whose value is a Map<String,String> with 0 or 1 entries — e.g. {\"secretName\": {\"path\": \"mysecret\"}} with only one key.","commonSituations":"users writing exposed secrets config from memory and providing only the secret path but not the key (or vice versa); copy-paste from non-Kubernetes examples where secrets are single strings; JSON/YAML edit dropping one of the two fields.","solutions":["Provide both required keys in each secret entry: {\"<secretId>\": {\"id\": \"<k8s-secret-name>\", \"key\": \"<key-inside-secret>\"}}","Check which entry has size < 2 in your secrets map and add the missing id or key field","Validate the secrets map locally before submitting (see validationCode in defense)"],"exampleFix":"// before\nsecrets: {\"mysecret\": {\"id\": \"db-creds\"}}\n// after\nsecrets: {\"mysecret\": {\"id\": \"db-creds\", \"key\": \"password\"}}","handlingStrategy":"validation","validationCode":"void validateK8sSecrets(Map<String, Object> secrets) {\n    for (Map.Entry<String, Object> e : secrets.entrySet()) {\n        if (!(e.getValue() instanceof Map) || ((Map<?, ?>) e.getValue()).size() < 2) {\n            throw new IllegalArgumentException(\"Secret '\" + e.getKey() + \"' must have both id and key\");\n        }\n    }\n}","typeGuard":"static boolean isK8sSecretRef(Object v) {\n    return v instanceof Map<?, ?> m && m.size() >= 2 && m.containsKey(\"id\") && m.containsKey(\"key\");\n}","tryCatchPattern":"try {\n    admin.functions().createFunction(functionConfig);\n} catch (IllegalArgumentException e) {\n    // inspect e.getMessage(); fix secrets map entries to contain id+key\n}","preventionTips":["Always define exposed secrets as {\"id\": ..., \"key\": ...} maps for the K8s runtime","Validate function config JSON/YAML before submission in CI","Don't reuse ClearSecretsProvider-style plain secrets with the K8s configurator"],"tags":["kubernetes","secrets","validation","pulsar-functions"],"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-14T05:17:10.506Z"}