{"record":{"id":"4e810a801c5a8b3b","repo":"risingwavelabs/risingwave","slug":"either-assume-role-or-access-key-and-secret-key-mu","errorCode":null,"errorMessage":"Either assume_role or access_key and secret_key must be provided for Redshift COPY command","messagePattern":"Either assume_role or access_key and secret_key must be provided for Redshift COPY command","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/snowflake_redshift/redshift.rs","lineNumber":1010,"sourceCode":"\nfn build_copy_into_sql(\n    schema_name: Option<&str>,\n    table_name: &str,\n    manifest_dir: &str,\n    access_key: &Option<String>,\n    secret_key: &Option<String>,\n    assume_role: &Option<String>,\n) -> Result<String> {\n    let table_name = build_full_table_name(schema_name, table_name);\n    let credentials = if let Some(assume_role) = assume_role {\n        &format!(\"aws_iam_role={}\", assume_role)\n    } else if let (Some(access_key), Some(secret_key)) = (access_key, secret_key) {\n        &format!(\n            \"aws_access_key_id={};aws_secret_access_key={}\",\n            access_key, secret_key\n        )\n    } else {\n        return Err(SinkError::Config(anyhow!(\n            \"Either assume_role or access_key and secret_key must be provided for Redshift COPY command\"\n        )));\n    };\n    Ok(format!(\n        r#\"\n        COPY {table_name}\n        FROM '{manifest_dir}'\n        CREDENTIALS '{credentials}'\n        FORMAT AS JSON 'auto'\n        DATEFORMAT 'auto'\n        TIMEFORMAT 'auto'\n        MANIFEST;\n        \"#,\n        table_name = table_name,\n        manifest_dir = manifest_dir,\n        credentials = credentials\n    ))\n}","sourceCodeStart":992,"sourceCodeEnd":1028,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/snowflake_redshift/redshift.rs#L992-L1028","documentation":"The Redshift `COPY` command that loads staged S3 files into Redshift needs AWS credentials: either an `assume_role` ARN or an `access_key`/`secret_key` pair. `build_copy_into_sql` throws this Config error when neither form of credentials is present in the sink config, because the generated COPY statement's authorization clause would be empty.","triggerScenarios":"Calling `copy_into_from_s3_to_redshift` when the sink config lacks `aws.assume_role` and also lacks both `aws.access_key` and `aws.secret_key` (one alone is insufficient).","commonSituations":"User configures IAM role auth but forgets `aws.assume_role`; provides only `aws.access_key` without `aws.secret_key`; empty-string keys parsed as None after validation.","solutions":["Add `aws.assume_role = 'arn:aws:iam::<account>:role/<role>'` to the sink WITH options.","Or set both `aws.access_key` and `aws.secret_key` in the WITH options.","Verify keys aren't empty strings and that the WITH options were actually passed to the sink (recreate the sink if needed)."],"exampleFix":"// before\nWITH (connector='redshift', aws.access_key='AKIA...', type='append-only');\n// after\nWITH (connector='redshift', aws.access_key='AKIA...', aws.secret_key='...', type='append-only');","handlingStrategy":"validation","validationCode":"fn has_copy_auth(p: &BTreeMap<String, String>) -> bool {\n    p.contains_key(\"aws.assume_role\")\n        || (p.contains_key(\"aws.access_key\") && p.contains_key(\"aws.secret_key\"))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer assume_role over static keys; set aws.assume_role at creation.","Never supply access_key without secret_key (and vice versa)."],"tags":["rust","redshift","aws","credentials","copy"],"backgroundTag":"missing-credentials","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}