{"record":{"id":"c5b57efd7f725283","repo":"databendlabs/databend","slug":"s3-session-token-requires-s3-access-key-id-and-s3","errorCode":null,"errorMessage":"s3.session-token requires s3.access-key-id and s3.secret-access-key","messagePattern":"s3\\.session-token requires s3\\.access-key-id and s3\\.secret-access-key","errorType":"validation","errorClass":"InvalidInput","httpStatus":null,"severity":"error","filePath":"src/common/storage/src/operator.rs","lineNumber":754,"sourceCode":"    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()))?;\n\n        let scheme = url.scheme();\n\n        // Handle file:// and memory:// URIs which don't have a host/bucket\n        let is_local_scheme = matches!(scheme, \"file\" | \"memory\" | \"\");\n        let (bucket, relative_path_pos) = if is_local_scheme {\n            // For file:// URIs, the path starts after \"file://\"","sourceCodeStart":736,"sourceCodeEnd":772,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/storage/src/operator.rs#L736-L772","documentation":"The same validator rejects an S3 session token supplied without the key pair it belongs to. s3.session-token is a temporary credential that is only meaningful together with s3.access-key-id and s3.secret-access-key, so having the token alone is treated as InvalidInput.","triggerScenarios":"build_operator -> validate_s3_credentials where props contains s3.session-token but is missing s3.access-key-id or s3.secret-access-key (or both).","commonSituations":"Users configuring STS/temporary credentials who paste the session token but omit the associated access/secret keys; automation that injects only the session token from an assumed-role secret; typo'd key names for the access/secret pair so they are not recognized.","solutions":["Add both s3.access-key-id and s3.secret-access-key alongside s3.session-token; a session token is only valid with its key pair.","If you do not need static temporary credentials, remove s3.session-token and rely on the ambient credential chain (IAM role).","Double-check key spelling/whitespace in the properties map so the access/secret keys are actually recognized."],"exampleFix":"// before\nprops.insert(\"s3.session-token\".into(), \"tok...\".into());\n// after\nprops.insert(\"s3.access-key-id\".into(), \"AKIA...\".into());\nprops.insert(\"s3.secret-access-key\".into(), \"secret...\".into());\nprops.insert(\"s3.session-token\".into(), \"tok...\".into());","handlingStrategy":"validation","validationCode":"let has_token = props.contains_key(\"s3.session-token\");\nlet has_pair = props.contains_key(\"s3.access-key-id\") && props.contains_key(\"s3.secret-access-key\");\nif has_token && !has_pair {\n    return Err(\"s3.session-token requires s3.access-key-id and s3.secret-access-key\".into());\n}","typeGuard":null,"tryCatchPattern":"// catch and surface as a credentials-completeness problem\nif let Err(e) = file_io.get_operator_path(location) {\n    if e.message().contains(\"session-token requires\") {\n        return Err(ConfigError::SessionTokenWithoutKeys);\n    }\n    return Err(e);\n}","preventionTips":["Inject all three temporary-credential fields (key id, secret, token) from the same secret store entry.","Verify key names exactly match 's3.access-key-id' / 's3.secret-access-key' — typos silently drop them.","Rotate temporary credentials as a complete set, never the token alone."],"tags":["storage","s3","credentials","iceberg","session-token"],"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"}