rustfs/rustfs · error
Only one Transition action per lifecycle rule is supported
Error message
Only one Transition action per lifecycle rule is supported
What it means
Thrown during lifecycle rule validation when a rule carries more than one Transition action. The server restricts each lifecycle rule to at most one Transition, matching the S3 single-transition-per-rule model. The error is an io::Error with ErrorKind::InvalidInput and message ERR_LIFECYCLE_MULTIPLE_TRANSITIONS.
Source
Thrown at crates/lifecycle/src/core.rs:561
// integer (>= 1); AWS rejects 0 with InvalidArgument.
if let Some(noncurrent_version_expiration) = &r.noncurrent_version_expiration
&& let Some(noncurrent_days) = noncurrent_version_expiration.noncurrent_days
&& noncurrent_days < 1
{
return Err(std::io::Error::other(ERR_LIFECYCLE_INVALID_NONCURRENT_EXPIRATION_DAYS));
}
// S3 requires AbortIncompleteMultipartUpload.DaysAfterInitiation to be present
// and a positive integer (>= 1); AWS rejects 0 (and a missing value) with
// InvalidArgument.
if let Some(abort_incomplete_multipart_upload) = &r.abort_incomplete_multipart_upload {
match abort_incomplete_multipart_upload.days_after_initiation {
Some(days) if days >= 1 => {}
_ => return Err(std::io::Error::other(ERR_LIFECYCLE_INVALID_ABORT_INCOMPLETE_MPU_DAYS)),
}
}
if let Some(transitions) = &r.transitions {
if transitions.len() > 1 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, ERR_LIFECYCLE_MULTIPLE_TRANSITIONS));
}
for transition in transitions {
TransitionOps::validate(transition)?;
}
}
if let Some(noncurrent_transitions) = &r.noncurrent_version_transitions {
if noncurrent_transitions.len() > 1 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
ERR_LIFECYCLE_MULTIPLE_NONCURRENT_TRANSITIONS,
));
}
for transition in noncurrent_transitions {
NoncurrentVersionTransitionOps::validate(transition)?;
}
}
if let Some(id) = &r.id {
if id.is_empty() {View on GitHub (pinned to 5dca076efe)
Solutions
- Split the rule so each Transition lives in its own lifecycle rule with an identical filter
- Keep only the single highest-priority transition in this rule
- Check transitions.len() <= 1 before submitting the configuration
Example fix
// before
{ "Rules": [{ "Transitions": [{"Days":30,"StorageClass":"GLACIER"},{"Days":60,"StorageClass":"DEEP_ARCHIVE"}] }]
// after
{ "Rules": [
{ "ID":"t1", "Transitions": [{"Days":30,"StorageClass":"GLACIER"}] },
{ "ID":"t2", "Transitions": [{"Days":60,"StorageClass":"DEEP_ARCHIVE"}] }
]} Defensive patterns
Strategy: validation
Validate before calling
fn single_transition(r: &Rule) -> bool { r.transitions.as_ref().map_or(true, |t| t.len() <= 1) } Try / catch
match config.validate() { Err(e) if e.to_string().contains("Only one Transition") => split rule and retry, ... } Prevention
- Generate one rule per Transition when exporting from other clouds
- Assert transitions length before PUT
- Keep transition tiering in dedicated rules with copied filters
When it happens
Trigger: Calling validate (lifecycle PUT/import) on a rule where the transitions array has len() > 1, e.g. two Transition entries with different storage classes or days.
Common situations: Merging lifecycle policies from multiple clouds (AWS console exports normally split transitions into separate rules); bulk config generators that append a transition per storage class; converting JSONata/manual policies that allowed multiple transitions.
Related errors
- InvalidInput
- Filter must have at most one of Prefix, Tag, ObjectSizeGreat
- Expiration cannot specify both Days and Date
- Only one NoncurrentVersionTransition action per lifecycle ru
- Configuration error: {0}
AI-assisted analysis of rustfs/rustfs@5dca076efe (2026-09-06).
Data as JSON: /api/errors/e1a62860e0d4cf7f.
Report an issue: GitHub.