dani-garcia/vaultwarden · error · ConfigInvalid
S3 support is not enabled
Error message
S3 support is not enabled
What it means
operator_for_path returns an OpenDAL config-invalid error when a storage path uses the s3:// scheme but the binary was compiled without the 's3' cargo feature. S3 storage code is cfg-gated out, so the scheme is unsupported.
Solutions
- Rebuild with S3 support: cargo build --release --features s3
- Use a Docker image/tag built with the s3 feature
- Switch attachments back to local filesystem storage (a normal directory path)
- Verify enabled features via the build logs or vaultwarden's version/diagnostics endpoint
Example fix
// before cargo build --release // after cargo build --release --features s3
Defensive patterns
Strategy: fallback
Validate before calling
if (path.startsWith('s3://') && !features.s3) {
throw new Error('s3:// path used but binary lacks the s3 feature; rebuild with --features s3');
} Type guard
function s3Supported(storagePath) { return !storagePath.startsWith('s3://') || buildFeatures.includes('s3'); } Try / catch
try { operator = operatorForPath(path); } catch (e) {
if (String(e).includes('S3 support is not enabled')) { logger.error('Rebuild with --features s3 or use local storage'); process.exit(1); }
throw e;
} Prevention
- Deploy images built with the feature set your config requires
- Gate s3:// config values behind a startup feature check
- Pin the Docker image variant that includes S3 support
- Run a startup smoke test that resolves the storage operator
When it happens
Trigger: Setting an attachment/storage path (e.g. ATTACHMENTS_FOLDER) to s3://... and running a Vaultwarden build compiled without --features s3 (or the prebuilt image without S3 support).
Common situations: Using the default Docker image that lacks the s3 feature; building from source without cargo build --features s3; switching storage backends without rebuilding the binary.
Related errors
AI-assisted analysis of dani-garcia/vaultwarden@cc67d644f6 (2026-09-19).
Data as JSON: /api/errors/28380954b137d4d7.
Report an issue: GitHub.
Appendix: source
Thrown at src/storage.rs:64
std::path::Path::new(path).file_name()?.to_str().map(str::to_owned)
}
pub(crate) fn is_fs_operator(operator: &opendal::Operator) -> bool {
operator.info().scheme() == opendal::services::FS_SCHEME
}
pub(crate) fn operator_for_path(path: &str) -> Result<opendal::Operator, crate::Error> {
// Cache of previously built operators by path
static OPERATORS_BY_PATH: LazyLock<dashmap::DashMap<String, opendal::Operator>> =
LazyLock::new(dashmap::DashMap::new);
if let Some(operator) = OPERATORS_BY_PATH.get(path) {
return Ok(operator.clone());
}
let operator = if path.starts_with("s3://") {
#[cfg(not(s3))]
return Err(opendal::Error::new(opendal::ErrorKind::ConfigInvalid, "S3 support is not enabled").into());
#[cfg(s3)]
s3::operator_for_path(path)?
} else {
let builder = opendal::services::Fs::default().root(path);
opendal::Operator::new(builder)?
};
OPERATORS_BY_PATH.insert(path.to_owned(), operator.clone());
Ok(operator)
}
#[cfg(s3)]
mod s3 {
use std::sync::LazyLock;
use opendal_http_transport_reqwest::ReqwestTransport;View on GitHub (pinned to cc67d644f6)