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

  1. Split the rule so each Transition lives in its own lifecycle rule with an identical filter
  2. Keep only the single highest-priority transition in this rule
  3. 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

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


AI-assisted analysis of rustfs/rustfs@5dca076efe (2026-09-06). Data as JSON: /api/errors/e1a62860e0d4cf7f. Report an issue: GitHub.