{"record":{"id":"10c77c71d3548f86","repo":"risingwavelabs/risingwave","slug":"adlsgen2-cannot-configure-both-shared-key-auth-a","errorCode":null,"errorMessage":"adlsgen2: cannot configure both shared-key auth (adlsgen2.account_key) and service-principal auth (adlsgen2.tenant_id / adlsgen2.client_id / adlsgen2.client_secret / adlsgen2.authority_host) simultaneously. Specify exactly one auth mode.","messagePattern":"adlsgen2: cannot configure both shared-key auth \\(adlsgen2\\.account_key\\) and service-principal auth \\(adlsgen2\\.tenant_id / adlsgen2\\.client_id / adlsgen2\\.client_secret / adlsgen2\\.authority_host\\) simultaneously\\. Specify exactly one auth mode\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/connector_common/iceberg/mod.rs","lineNumber":845,"sourceCode":"            // `adlsgen2.tenant_id = ''` (or a value with trailing `\\n` from a copy-paste)\n            // as `Some(\"...\")` which would pass `is_some()` but break downstream auth.\n            fn nonempty(v: &Option<String>) -> Option<&str> {\n                v.as_deref().filter(|s| !s.trim().is_empty())\n            }\n            let sp_tenant = nonempty(&self.adlsgen2_tenant_id);\n            let sp_client = nonempty(&self.adlsgen2_client_id);\n            let sp_secret = nonempty(&self.adlsgen2_client_secret);\n            let sp_authority = nonempty(&self.adlsgen2_authority_host);\n            let sk_account_name = nonempty(&self.adlsgen2_account_name);\n            let sk_account_key = nonempty(&self.adlsgen2_account_key);\n            let any_sp_field = sp_tenant.is_some()\n                || sp_client.is_some()\n                || sp_secret.is_some()\n                || sp_authority.is_some();\n            let all_sp_required = sp_tenant.is_some() && sp_client.is_some() && sp_secret.is_some();\n\n            if sk_account_key.is_some() && any_sp_field {\n                bail!(\n                    \"adlsgen2: cannot configure both shared-key auth \\\n                     (adlsgen2.account_key) and service-principal auth \\\n                     (adlsgen2.tenant_id / adlsgen2.client_id / adlsgen2.client_secret / \\\n                     adlsgen2.authority_host) simultaneously. Specify exactly one auth mode.\"\n                );\n            }\n            if any_sp_field && !all_sp_required {\n                bail!(\n                    \"adlsgen2: service-principal auth requires all three of \\\n                     adlsgen2.tenant_id, adlsgen2.client_id, and adlsgen2.client_secret \\\n                     to be set. (adlsgen2.authority_host is optional and defaults to the \\\n                     public Azure AAD endpoint.)\"\n                );\n            }\n            // Defense in depth: reqsign POSTs the OAuth token request — carrying the\n            // client_secret to this host. Require a bare https origin: no userinfo,\n            // no query, no fragment, and no path beyond \"/\". The value itself is not\n            // echoed into error messages in case a user pasted a secret by mistake.","sourceCodeStart":827,"sourceCodeEnd":863,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/connector_common/iceberg/mod.rs#L827-L863","documentation":"RisingWave's ADLS Gen2 connector for Iceberg tables supports two mutually exclusive auth modes: shared-key (account_key) and service-principal (OAuth with tenant_id/client_id/client_secret). This validation, run while assembling Iceberg catalog/file configs, rejects a with-props block that supplies fields for both modes at once, since the auth mode would be ambiguous.","triggerScenarios":"Calling CREATE SINK/TABLE with an adlsgen2 endpoint where with-props include adlsgen2.account_key together with any of adlsgen2.tenant_id, adlsgen2.client_id, adlsgen2.client_secret, or adlsgen2.authority_host.","commonSituations":"Copy-pasting a config template that lists all auth options; switching auth modes and leaving the old account_key in place; a shared team config accumulating credentials from both modes.","solutions":["Remove adlsgen2.account_key if you intend to use service-principal auth.","Remove the adlsgen2.tenant_id/client_id/client_secret/authority_host fields if you intend to use shared-key auth.","Ensure all four service-principal fields are present (authority_host optional) if keeping SP auth."],"exampleFix":"-- before\nWITH (\n  'adlsgen2.account_key' = 'xxx',\n  'adlsgen2.tenant_id' = 't',\n  'adlsgen2.client_id' = 'c',\n  'adlsgen2.client_secret' = 's'\n)\n-- after\nWITH (\n  'adlsgen2.tenant_id' = 't',\n  'adlsgen2.client_id' = 'c',\n  'adlsgen2.client_secret' = 's'\n)","handlingStrategy":"validation","validationCode":"-- run before CREATE\n-- ensure exactly one auth mode:\n-- either adlsgen2.account_key alone, or tenant_id+client_id+client_secret together\nSELECT\n  (account_key IS NOT NULL) XOR (tenant_id IS NOT NULL OR client_id IS NOT NULL OR client_secret IS NOT NULL OR authority_host IS NOT NULL) AS ok;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep one DDL template per auth mode and never merge them.","Grep your with-props for both account_key and tenant_id before submitting.","Remove stale fields when migrating auth modes."],"tags":["iceberg","adlsgen2","configuration","authentication"],"backgroundTag":"mutually-exclusive-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"}