{"record":{"id":"37c41a9a0b63f3df","repo":"risingwavelabs/risingwave","slug":"failed-to-close-manifest-writer","errorCode":null,"errorMessage":"Failed to close manifest writer: {}","messagePattern":"Failed to close manifest writer: (.+?)","errorType":"exception","errorClass":"SinkError::Redshift","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/snowflake_redshift/redshift.rs","lineNumber":527,"sourceCode":"\n    async fn write_manifest_to_s3(\n        s3_inner: &S3Common,\n        paths: Vec<String>,\n        table: &str,\n    ) -> Result<String> {\n        let manifest_entries: Vec<_> = paths\n            .into_iter()\n            .map(|path| json!({ \"url\": path, \"mandatory\": true }))\n            .collect();\n        let s3_operator = FileSink::<S3Sink>::new_s3_sink(s3_inner)?;\n        let (mut writer, manifest_path) =\n            build_opendal_writer_path(s3_inner, &s3_operator, Some(\"manifest\"), table).await?;\n        let manifest_json = json!({ \"entries\": manifest_entries });\n        let mut chunk_buf = BytesMut::new();\n        writeln!(chunk_buf, \"{}\", manifest_json).unwrap();\n        writer.write(chunk_buf.freeze()).await?;\n        writer.close().await.map_err(|e| {\n            SinkError::Redshift(anyhow!(\n                \"Failed to close manifest writer: {}\",\n                e.to_report_string()\n            ))\n        })?;\n        Ok(manifest_path)\n    }\n\n    pub async fn copy_into_from_s3_to_redshift(\n        client: &JdbcJniClient,\n        config: &RedShiftConfig,\n        s3_inner: &S3Common,\n        is_append_only: bool,\n        manifest: &str,\n    ) -> Result<()> {\n        let all_path = format!(\"s3://{}/{}\", s3_inner.bucket_name, manifest);\n\n        let (table, schema_name) = if is_append_only {\n            (&config.table, config.schema.as_deref())","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/snowflake_redshift/redshift.rs#L509-L545","documentation":"After writing the S3 manifest JSON entries via an OpenDAL writer, `writer.close()` failed; the sink wraps the error as a Redshift sink error. A failed close typically means the final bytes were not flushed/committed to S3, so the manifest may be missing or truncated and subsequent Redshift COPY will fail.","triggerScenarios":"Calling `write_manifest_to_s3` when S3 is unreachable, credentials are invalid, the bucket/prefix does not exist, or the connection drops during the writer's final flush.","commonSituations":"Expired AWS credentials or IAM policy missing s3:PutObject; wrong `s3.region`/endpoint in sink config; network partitions between the RisingWave node and S3 (or S3-compatible storage like MinIO).","solutions":["Check sink S3 config (bucket, region, endpoint, path) and AWS credentials/IAM permissions","Retry the sink; the manifest write is part of periodic commit and will be retried on the next epoch","Verify connectivity to the S3 endpoint from the compute node (e.g., MinIO address reachable)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check S3 connectivity/credentials before starting the sink\ns3_client.head_bucket().bucket(bucket).send().await?;","typeGuard":null,"tryCatchPattern":"match writer.close().await {\n    Ok(()) => (),\n    Err(e) => log::error!(\"manifest close failed: {}\", e.to_report_string()), // retry next epoch\n}","preventionTips":["Verify IAM permissions include s3:PutObject on the manifest prefix","Test S3 endpoint reachability from compute nodes","Use stable credentials (instance role) instead of long-lived keys"],"tags":["redshift","s3","opendal","network"],"backgroundTag":"http-request-failed","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"}