moghtech/komodo · error · anyhow::Error

Invalid container state

Error message

Invalid container state: {}

What it means

ContainerStateStatusEnum::from_str converts a Docker-reported container state string into the enum. If the string is not one of running/paused/restarting/removing/exited/dead, it fails with anyhow, embedding the offending string in the message. This guards against unknown or malformed state values coming from Docker.

Solutions

  1. Log the exact string shown in the error message and compare against the supported set: running, paused, restarting, removing, exited, dead
  2. Normalize input to lowercase and trim whitespace before calling from_str
  3. Check Docker Engine version; if it reports a new state, update/patch the Komodo client enum to support it
  4. Map legacy states like 'created' to a supported variant before parsing

Example fix

// before
let state = ContainerStateStatusEnum::from_str(raw)?;
// after
let state = ContainerStateStatusEnum::from_str(raw.trim().to_lowercase().as_str())
    .or_else(|_| match raw.trim().to_lowercase().as_str() {
        "created" => Ok(ContainerStateStatusEnum::Exited), // legacy mapping
        other => Err(anyhow!("unmapped container state: {other}")),
    })?;
Defensive patterns

Strategy: validation

Validate before calling

const VALID: [&str; 6] = ["running","paused","restarting","removing","exited","dead"];
fn is_valid_state(s: &str) -> bool { VALID.contains(&s.trim().to_lowercase().as_str()) }
if !is_valid_state(raw) { return Err(anyhow!("unsupported container state: {raw}")); }

Type guard

fn is_known_state(s: &str) -> bool {
    matches!(s.trim().to_lowercase().as_str(), "running"|"paused"|"restarting"|"removing"|"exited"|"dead")
}

Prevention

When it happens

Trigger: Deserializing a container state string that is not one of the six known values, e.g. an empty string, whitespace, capitalized text like 'Running', or a state newly introduced by a Docker engine version.

Common situations: Parsing Docker inspect/API responses from a newer Docker Engine that emits a state the client SDK doesn't know; accidentally passing a status (e.g. 'created', 'Up 2 minutes') instead of a state enum string.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.


AI-assisted analysis of moghtech/komodo@780ac68b99 (2026-09-08). Data as JSON: /api/errors/e46e262fe8d18981. Report an issue: GitHub.

Appendix: source

Thrown at client/core/rs/src/entities/docker/container.rs:344

  #[default]
  #[serde(rename = "")]
  #[strum(serialize = "")]
  Empty,
}

impl ::std::str::FromStr for ContainerStateStatusEnum {
  type Err = anyhow::Error;
  fn from_str(s: &str) -> Result<Self, Self::Err> {
    match s {
      "" => Ok(ContainerStateStatusEnum::Empty),
      "created" => Ok(ContainerStateStatusEnum::Created),
      "running" => Ok(ContainerStateStatusEnum::Running),
      "paused" => Ok(ContainerStateStatusEnum::Paused),
      "restarting" => Ok(ContainerStateStatusEnum::Restarting),
      "removing" => Ok(ContainerStateStatusEnum::Removing),
      "exited" => Ok(ContainerStateStatusEnum::Exited),
      "dead" => Ok(ContainerStateStatusEnum::Dead),
      x => Err(anyhow!("Invalid container state: {}", x)),
    }
  }
}

/// Health stores information about the container's healthcheck results.
#[typeshare]
#[derive(
  Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct ContainerHealth {
  /// Status is one of `none`, `starting`, `healthy` or `unhealthy`  - \"none\"      Indicates there is no healthcheck - \"starting\"  Starting indicates that the container is not yet ready - \"healthy\"   Healthy indicates that the container is running correctly - \"unhealthy\" Unhealthy indicates that the container has a problem
  #[serde(default, rename = "Status")]
  pub status: HealthStatusEnum,

  /// FailingStreak is the number of consecutive failures
  #[serde(rename = "FailingStreak")]
  pub failing_streak: Option<I64>,

View on GitHub (pinned to 780ac68b99)