risingwavelabs/risingwave · error · ConnectorError

the key {} is set both in plaintext and secret

Error message

the key {} is set both in plaintext and secret

What it means

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.

Source

Thrown at src/connector/src/with_options.rs:289

        &self.secret_ref
    }

    pub fn handle_update(
        &mut self,
        update_alter_props: BTreeMap<String, String>,
        update_alter_secret_refs: BTreeMap<String, PbSecretRef>,
    ) -> ConnectorResult<(Vec<SecretId>, Vec<SecretId>)> {
        let old_secret_ids = self
            .secret_ref
            .values()
            .map(|secret_ref| secret_ref.secret_id)
            .collect::<BTreeSet<_>>();

        // make sure the key in update_alter_props and update_alter_secret_refs not collide
        for key in update_alter_props.keys() {
            if update_alter_secret_refs.contains_key(key) {
                return Err(
                    anyhow::anyhow!("the key {} is set both in plaintext and secret", key).into(),
                );
            }
        }

        // remove legacy key if it's set in both plaintext and secret
        // When a property changes from secret to plaintext, remove the old secret dependency
        for k in update_alter_props.keys() {
            self.secret_ref.remove(k);
        }

        // Handle secret ref updates
        for k in update_alter_secret_refs.keys() {
            // Remove any plaintext value for this key
            self.inner.remove(k);
        }

        self.inner.extend(update_alter_props);
        self.secret_ref.extend(update_alter_secret_refs);

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove the duplicate key from either the plaintext update or the secret-ref update so each key appears in exactly one map
  2. To move a key from secret to plaintext (or back), update only the target form in a single ALTER
  3. Split conflicting changes into sequential statements

Example fix

// before
ALTER SOURCE s SET properties.access_key='abc' WITH(secret_ref_access_key='secret-1')
// after
ALTER SOURCE s WITH(secret_ref_access_key='secret-1')
Defensive patterns

Strategy: validation

Validate before calling

function validateNoPlaintextSecretCollision(plainProps, secretRefs) {
  for (const k of Object.keys(plainProps)) {
    if (k in secretRefs) throw new Error(`key ${k} set both in plaintext and secret`);
  }
}

Type guard

const hasNoCollision = (p, s) => !Object.keys(p).some(k => k in s);

Try / catch

try { handleUpdate(plainUpdates, secretUpdates); } catch (e) { if (String(e).includes('set both in plaintext and secret')) { dropDuplicateKeyAndRetry(); } else { throw e; } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/f9137ca20326bb74. Report an issue: GitHub.