jdx/mise · error · eyre::Report
container inspection returned an unexpected number of rows
Error message
container inspection returned an unexpected number of rows
What it means
To compare deployed config hashes, mise lists the project's containers, then runs `docker inspect --format '{{json .Config.Labels}}'` over their IDs and expects exactly one JSON line per container. A row-count mismatch means the container set changed between the two engine calls (containers created, removed, or restarting), so label extraction is abandoned rather than misattributing hashes.
Source
Thrown at src/system/compose.rs:554
fn add_container_hashes(&self, containers: &mut [ComposeContainer]) -> Result<()> {
if containers.is_empty() {
return Ok(());
}
let ids = containers
.iter()
.map(|container| container.id.clone())
.collect::<Vec<_>>();
let mut args = vec![
"inspect".to_string(),
"--format".to_string(),
"{{json .Config.Labels}}".to_string(),
];
args.extend(ids);
let output = self.engine_output(&args)?;
let lines = output.lines().collect::<Vec<_>>();
if lines.len() != containers.len() {
bail!("container inspection returned an unexpected number of rows");
}
for (container, line) in containers.iter_mut().zip(lines) {
let labels: HashMap<String, String> = serde_json::from_str(line)?;
container.config_hash = labels.get("com.docker.compose.config-hash").cloned();
}
Ok(())
}
fn desired(&self) -> String {
match self.state {
ComposeState::Running => format!(
"running{}; pull {:?}; build {:?}; recreate {:?}; wait {}",
selected_suffix(&self.services),
self.pull,
self.build,
self.recreate,
self.wait
)View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Re-run `mise bootstrap compose apply` — the mismatch is transient and the next classification pass usually succeeds.
- Stabilize the project first: fix crash-looping services or pause competing automation (watchtower/CI) during converge.
- If it persists, capture `docker compose -p <project> ps -a` twice in a row and diff to confirm what is churning.
Defensive patterns
Strategy: retry
Try / catch
for attempt in 0..3 {
match converge_project(&request) {
Ok(()) => break,
Err(e) if e.to_string().contains("unexpected number of rows") && attempt < 2 => {
std::thread::sleep(std::time::Duration::from_millis(500)); // container set churned; re-inspect
}
Err(e) => return Err(e),
}
} Prevention
- Stop crash-looping services and competing automation (watchtower, CI deploys) before converging.
- Treat a one-off mismatch as transient: rerun `mise bootstrap compose apply` before debugging.
When it happens
Trigger: Containers appear or vanish between mise's ps/inspect calls: a `restart: always` crash loop churning containers, another operator or CI job concurrently running compose on the same project, or oneshot containers exiting in that window.
Common situations: Unstable services during converge (failing healthchecks with auto-restart); overlapping automation (watchtower, CI deploy, a teammate) touching the same compose project; heavily loaded engines with slow inspect responses.
Related errors
- refusing unsafe change to bootstrap compose project '{}'; in
- bootstrap compose project '{name}' services cannot be combin
- services not found in compose config: {}
- compose command failed with {status}
- legacy 'docker-compose' v1 is unsupported; install Docker Co
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/1d93fa1ac8fcd854.
Report an issue: GitHub.