block/buzz · critical · anyhow::Error
invalid media config
Error message
invalid media config: {e} What it means
Fatal startup error: MediaConfig::validate() rejected the media/S3 configuration before MediaStorage construction. The checks are concrete: public_base_url must end with /media and must not end with /; max_image_bytes > 0; max_gif_bytes in (0, max_image_bytes]; max_video_bytes and max_file_bytes > 0; an upload IP header without upload records enabled is rejected; a port header without an IP header is rejected.
Solutions
- Set BUZZ_MEDIA_PUBLIC_BASE_URL to a URL ending exactly with /media and no trailing slash (e.g. https://cdn.example.com/media)
- Make all size limits positive integers, with max_gif_bytes <= max_image_bytes
- Either enable BUZZ_MEDIA_UPLOAD_RECORDS=true when an upload IP header is configured, or unset the header
- Unset the port header unless the IP header is also set
Example fix
# before export BUZZ_MEDIA_PUBLIC_BASE_URL=https://cdn.example.com export BUZZ_MAX_IMAGE_BYTES=0 # after export BUZZ_MEDIA_PUBLIC_BASE_URL=https://cdn.example.com/media export BUZZ_MAX_IMAGE_BYTES=52428800
Defensive patterns
Strategy: validation
Validate before calling
# Deploy-time guard:
: "${BUZZ_MEDIA_PUBLIC_BASE_URL:?}"
case "$BUZZ_MEDIA_PUBLIC_BASE_URL" in
*/media) ;; # ok, no trailing slash beyond /media
*) echo 'BUZZ_MEDIA_PUBLIC_BASE_URL must end with /media'; exit 1 ;;
esac Prevention
- Keep public_base_url ending exactly with /media and never with a trailing slash
- Use positive sizes for all upload limits and keep gif <= image limit
- Enable BUZZ_MEDIA_UPLOAD_RECORDS whenever an upload IP header is configured
When it happens
Trigger: BUZZ_MEDIA_PUBLIC_BASE_URL missing the /media suffix or carrying a trailing slash; a size limit var set to 0; max GIF size larger than max image size; BUZZ_MEDIA_UPLOAD_IP_HEADER set while BUZZ_MEDIA_UPLOAD_RECORDS is not enabled; BUZZ_MEDIA_UPLOAD_PORT_HEADER set without the IP header.
Common situations: Pointing public_base_url at a CDN root instead of the /media path; tuning size limits to "unlimited" by entering 0 (which actually means invalid); enabling IP reporting without turning on the records that store it.
Related errors
- failed to initialize media storage
- BUZZ_RELAY_PRIVATE_KEY is required when…
- Cannot derive a community host from BUZZ_RELAY_URL
- Configuration error
- git conformance probe failed
AI-assisted analysis of block/buzz@ef2aa1ae38 (2026-08-20).
Data as JSON: /api/errors/862c86efb6a85b2f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/buzz-relay/src/main.rs:512
.unwrap_or(&config.database_url);
let search_pool = sqlx::postgres::PgPoolOptions::new()
.connect(search_db_url)
.await
.map_err(|e| anyhow::anyhow!("Search DB connection failed: {e}"))?;
let search_metrics_pool = search_pool.clone();
let search = SearchService::new(search_pool);
info!(
replica = config.read_database_url.is_some(),
"Search service ready (Postgres FTS)"
);
let workflow_config = buzz_workflow::WorkflowConfig::default();
let workflow_engine = Arc::new(WorkflowEngine::new(db.clone(), workflow_config));
config
.media
.validate()
.map_err(|e| anyhow::anyhow!("invalid media config: {e}"))?;
let media_storage = buzz_media::MediaStorage::new(&config.media)
.map_err(|e| anyhow::anyhow!("failed to initialize media storage: {e}"))?;
info!("Media storage connected");
let (app_state, audit_shutdown) = AppState::new(
config.clone(),
db,
redis_health_pool,
audit,
pubsub,
auth,
search,
Arc::clone(&workflow_engine),
relay_keypair,
media_storage,
);
let state = Arc::new(app_state);
View on GitHub (pinned to ef2aa1ae38)