{"record":{"id":"f9137ca20326bb74","repo":"risingwavelabs/risingwave","slug":"the-key-is-set-both-in-plaintext-and-secret","errorCode":null,"errorMessage":"the key {} is set both in plaintext and secret","messagePattern":"the key (.+?) is set both in plaintext and secret","errorType":"validation","errorClass":"ConnectorError","httpStatus":null,"severity":"error","filePath":"src/connector/src/with_options.rs","lineNumber":289,"sourceCode":"        &self.secret_ref\n    }\n\n    pub fn handle_update(\n        &mut self,\n        update_alter_props: BTreeMap<String, String>,\n        update_alter_secret_refs: BTreeMap<String, PbSecretRef>,\n    ) -> ConnectorResult<(Vec<SecretId>, Vec<SecretId>)> {\n        let old_secret_ids = self\n            .secret_ref\n            .values()\n            .map(|secret_ref| secret_ref.secret_id)\n            .collect::<BTreeSet<_>>();\n\n        // make sure the key in update_alter_props and update_alter_secret_refs not collide\n        for key in update_alter_props.keys() {\n            if update_alter_secret_refs.contains_key(key) {\n                return Err(\n                    anyhow::anyhow!(\"the key {} is set both in plaintext and secret\", key).into(),\n                );\n            }\n        }\n\n        // remove legacy key if it's set in both plaintext and secret\n        // When a property changes from secret to plaintext, remove the old secret dependency\n        for k in update_alter_props.keys() {\n            self.secret_ref.remove(k);\n        }\n\n        // Handle secret ref updates\n        for k in update_alter_secret_refs.keys() {\n            // Remove any plaintext value for this key\n            self.inner.remove(k);\n        }\n\n        self.inner.extend(update_alter_props);\n        self.secret_ref.extend(update_alter_secret_refs);","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/with_options.rs#L271-L307","documentation":"When updating a source's properties, each key may be changed either as a plaintext property or as a secret reference, but not both at once. handle_update iterates the plaintext update map and rejects any key that also appears in the secret-reference map, since the two would conflict and the final value would be ambiguous.","triggerScenarios":"Calling handle_update (via ALTER SOURCE ... or update_source_props_by_source_id / update_connection_and_dependent_objects_props) where an ALTER supplies the same key both as a plain property and as a secret_ref, e.g. setting 'access_key' in WITH properties and also as a secret reference in the same statement.","commonSituations":"Migrating a property from plaintext to secret (or vice versa) by specifying both forms in one statement instead of changing only one; tooling that appends properties without deduplicating against secret refs.","solutions":["Remove the duplicate key from either the plaintext update or the secret-ref update so each key appears in exactly one map","To move a key from secret to plaintext (or back), update only the target form in a single ALTER","Split conflicting changes into sequential statements"],"exampleFix":"// before\nALTER SOURCE s SET properties.access_key='abc' WITH(secret_ref_access_key='secret-1')\n// after\nALTER SOURCE s WITH(secret_ref_access_key='secret-1')","handlingStrategy":"validation","validationCode":"function validateNoPlaintextSecretCollision(plainProps, secretRefs) {\n  for (const k of Object.keys(plainProps)) {\n    if (k in secretRefs) throw new Error(`key ${k} set both in plaintext and secret`);\n  }\n}","typeGuard":"const hasNoCollision = (p, s) => !Object.keys(p).some(k => k in s);","tryCatchPattern":"try { handleUpdate(plainUpdates, secretUpdates); } catch (e) { if (String(e).includes('set both in plaintext and secret')) { dropDuplicateKeyAndRetry(); } else { throw e; } }","preventionTips":["Decide per key whether it lives in plaintext or secret and change only that form","Deduplicate ALTER statements before applying property updates","When migrating keys between plaintext and secret, issue a single update targeting the new form only"],"tags":["secrets","config","conflict"],"backgroundTag":"conflicting-config-options","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}