cloudflare/pingora · error · std::io::Error
dial9 s3 bucket must not be empty
Error message
dial9 s3 bucket must not be empty
What it means
When pingora-runtime is built with the dial9-worker-s3 feature and opts.s3_upload is Some, build_dial9_runtime() validates the S3 upload settings before wiring the background uploader (lib.rs:365). The bucket name is trimmed and must be non-empty, otherwise construction returns ErrorKind::InvalidInput with 'dial9 s3 bucket must not be empty'.
Source
Thrown at pingora-runtime/src/lib.rs:365
let writer = RotatingWriter::builder()
.base_path(opts.trace_path.clone())
.max_file_size(opts.max_file_size)
.max_total_size(opts.max_total_size)
.maybe_rotation_period(opts.rotation_period)
.build()?;
let mut traced = TracedRuntime::builder()
.with_trace_path(opts.trace_path.clone())
.with_runtime_name(runtime_name)
.with_task_tracking(opts.task_tracking);
if let Some(worker_poll_interval) = opts.worker_poll_interval {
traced = traced.with_worker_poll_interval(worker_poll_interval);
}
#[cfg(feature = "dial9-worker-s3")]
if let Some(s3_upload) = &opts.s3_upload {
if s3_upload.bucket.trim().is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"dial9 s3 bucket must not be empty",
));
}
if s3_upload.service_name.trim().is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"dial9 s3 service_name must not be empty",
));
}
let s3_config = dial9_tokio_telemetry::background_task::s3::S3Config::builder()
.bucket(s3_upload.bucket.clone())
.service_name(s3_upload.service_name.clone())
.maybe_prefix(s3_upload.prefix.clone())
.maybe_region(s3_upload.region.clone())
.maybe_instance_path(s3_upload.instance_path.clone());
let traced = traced.with_s3_uploader(s3_config.build());
if let Some(client) = s3_upload.client.clone() {View on GitHub (pinned to 0046038bd4)
Solutions
- Set a real bucket name, e.g. bucket: "my-trace-bucket".to_string()
- Also set a non-empty service_name (checked immediately after) and prefer explicit region/credentials configuration
- Fail config loading early when the S3 block is present but bucket/service_name are blank
Example fix
// before
s3_upload: Some(S3Upload { bucket: String::new(), service_name: "s3".into(), ..Default::default() })
// after
s3_upload: Some(S3Upload { bucket: "my-trace-bucket".into(), service_name: "s3".into(), ..Default::default() }) Defensive patterns
Strategy: validation
Validate before calling
if let Some(s3) = &opts.s3_upload {
if s3.bucket.trim().is_empty() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "s3 bucket must be set"));
}
} Prevention
- Fail config loading when the S3 upload block exists but bucket is blank
- Resolve the bucket from an env var with a required-value check, not a silent empty default
- Trim and assert both bucket and service_name together before building the runtime
When it happens
Trigger: Dial9RuntimeOpts with s3_upload = Some(S3Upload { bucket: "" or " ", .. }) while the dial9-worker-s3 feature is compiled in; the check runs during dial9 runtime construction at startup.
Common situations: S3 upload enabled in config but the bucket key left blank; bucket populated from an env var that is unset in the environment being deployed; whitespace-only values from templated config files.
Related errors
- dial9 s3 service_name must not be empty
- dial9 max_file_size must be greater than zero
- dial9 max_total_size must be greater than zero
- dial9 max_file_size must be less than or equal to max_total_
- dial9 worker_poll_interval must be greater than zero
AI-assisted analysis of cloudflare/pingora@0046038bd4 (2026-08-16).
Data as JSON: /api/errors/ee4cd6147cbc0b82.
Report an issue: GitHub.