{"record":{"id":"84f3fe485db33e3e","repo":"databendlabs/databend","slug":"s3-access-key-id-and-s3-secret-access-key-must-be","errorCode":null,"errorMessage":"s3.access-key-id and s3.secret-access-key must be configured together","messagePattern":"s3\\.access-key-id and s3\\.secret-access-key must be configured together","errorType":"validation","errorClass":"InvalidInput","httpStatus":null,"severity":"error","filePath":"src/common/storage/src/operator.rs","lineNumber":747,"sourceCode":"\npub struct IcebergFileIO {\n    scheme: String,\n    props: std::collections::HashMap<String, String>,\n}\n\nimpl IcebergFileIO {\n    pub fn new(file_io: iceberg::io::FileIO) -> Self {\n        let (scheme, props, _extensions) = file_io.into_builder().into_parts();\n        Self { scheme, props }\n    }\n\n    fn validate_s3_credentials(&self) -> Result<()> {\n        let has_access_key = self.props.contains_key(\"s3.access-key-id\");\n        let has_secret_key = self.props.contains_key(\"s3.secret-access-key\");\n        let has_session_token = self.props.contains_key(\"s3.session-token\");\n\n        if has_access_key != has_secret_key {\n            return Err(Error::new(\n                ErrorKind::InvalidInput,\n                \"s3.access-key-id and s3.secret-access-key must be configured together\",\n            ));\n        }\n\n        if has_session_token && !(has_access_key && has_secret_key) {\n            return Err(Error::new(\n                ErrorKind::InvalidInput,\n                \"s3.session-token requires s3.access-key-id and s3.secret-access-key\",\n            ));\n        }\n\n        Ok(())\n    }\n\n    fn build_operator(&self, location: &str) -> Result<(Operator, usize)> {\n        let url = url::Url::parse(location)\n            .map_err(|e| Error::new(ErrorKind::InvalidInput, e.to_string()))?;","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/storage/src/operator.rs#L729-L765","documentation":"IcebergFileIO::validate_s3_credentials enforces that S3 static credentials are complete before building an opendal Operator. The props map from the iceberg FileIO must contain s3.access-key-id and s3.secret-access-key as a pair; providing only one is rejected as InvalidInput because partial credentials can never authenticate.","triggerScenarios":"Building an IcebergFileIO operator (build_operator -> validate_s3_credentials) whose FileIO props contain s3.access-key-id but no s3.secret-access-key, or vice versa.","commonSituations":"Misconfigured S3 catalog/table properties where a user sets only the access key ID; credential values injected from environment variables or secrets where one of the two failed to resolve; copy-pasting a properties block and dropping one line.","solutions":["Add the missing counterpart property: set both s3.access-key-id and s3.secret-access-key in the FileIO/catalog properties.","If you intended no static credentials (e.g. IAM role or anonymous access), remove the half-configured key entirely instead of setting only one.","Verify the properties source (catalog config, environment substitution, secret mount) actually injects both values."],"exampleFix":"// before (iceberg FileIO props)\nprops.insert(\"s3.access-key-id\".into(), \"AKIA...\".into());\n// after\nprops.insert(\"s3.access-key-id\".into(), \"AKIA...\".into());\nprops.insert(\"s3.secret-access-key\".into(), \"secret...\".into());","handlingStrategy":"validation","validationCode":"let has_ak = props.contains_key(\"s3.access-key-id\");\nlet has_sk = props.contains_key(\"s3.secret-access-key\");\nif has_ak != has_sk {\n    return Err(\"s3.access-key-id and s3.secret-access-key must be set together\".into());\n}","typeGuard":null,"tryCatchPattern":"// map the InvalidInput to a config error with context\nmatch file_io.get_operator_path(location) {\n    Err(e) if e.message().contains(\"configured together\") => {\n        return Err(ConfigError::IncompleteS3Credentials(location.to_string()));\n    }\n    other => other,\n}","preventionTips":["Always set S3 credentials as a matched pair, sourced from the same secret/config block.","Prefer IAM roles / credential chain over static keys when possible.","Validate FileIO props at catalog configuration time, before table operations."],"tags":["storage","s3","credentials","iceberg","configuration"],"backgroundTag":"missing-credentials","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}