jlcodes99/cockpit-tools · error

( )

Error message

{} ({})

What it means

Generic process-teardown failure formatted as "{failure_message} ({pid_list})" in the shared close-and-verify routine of process_core_matching: after graceful close, close_pids, and a forced retry, collect_remaining_entries still finds processes matching the target dirs. The message names the failing operation and lists the PIDs that remain alive.

Solutions

  1. Manually terminate the PIDs listed in parentheses in the message (kill -9 / Task Manager), then retry.
  2. Check whether a supervisor or auto-restart mechanism relaunches the processes; disable it before closing.
  3. Run with sufficient privileges (elevate) so signals can reach processes owned by other accounts.
  4. Increase the timeout or repeat the close once — the routine already retries force-kill with a 6s window.
  5. Inspect the '[<prefix>] still_running_entries=' log line to see which processes/dirs remained and why.

Example fix

// before: assuming close success
assert!(close_managed_instances_common().is_ok());
// after: parse remaining PIDs from the failure and escalate
match close_managed_instances_common() {
    Err(msg) => {
        let pids = extract_parenthesized_pids(&msg);
        force_kill_elevated(&pids); // taskkill /F /PID ... or kill -9
    }
    Ok(_) => {}
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before closing, verify no supervisor will respawn the targets
fn no_respawn_expected(match_cmdline: &str) -> bool {
    !list_running_processes().iter().any(|p| {
        p.cmdline.contains("supervisor") && p.cmdline.contains(match_cmdline)
    })
}

Type guard

fn parse_remaining_pids(err_msg: &str) -> Vec<u32> {
    err_msg.rsplit_once('(').and_then(|(_, tail)| tail.trim_end_matches(')').split_whitespace().next())
        .map(|s| s.split(',').filter_map(|p| p.trim().parse().ok()).collect())
        .unwrap_or_default()
}

Try / catch

match close_managed_instances() {
    Err(msg) => {
        let pids = parse_remaining_pids(&msg);
        eprintln!("teardown failed, force-killing {:?}", pids);
        force_kill_elevated(&pids); // taskkill /F or kill -9
    }
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Calling any managed-instance close flow (Codex/Trae/opencode instances) when, even after two close_pids passes (graceful wait + 6s force retry), processes identified via target_dirs are still running — blocked signals, elevation mismatch, or respawned child processes.

Common situations: Supervisor/parent process respawning killed children; processes running under a different user; hung processes in uninterruptible wait; file locks held in the target dirs keeping processes alive; insufficient permissions without elevation.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/b43229c544e9d721. Report an issue: GitHub.

Appendix: source

Thrown at crates/cockpit-core/src/modules/process_core_matching.rs:3306

            if let Err(err) = close_pids(&remaining_pids, 6) {
                crate::modules::logger::log_warn(&format!(
                    "[{}] retry close_pids returned error: {}",
                    log_prefix, err
                ));
            }
            remaining_entries = collect_remaining_entries(&target_dirs);
        }
    }

    if !remaining_entries.is_empty() {
        let remaining_pids = collect_remaining_pids(&remaining_entries);
        if let Some(detail_logger_fn) = detail_logger {
            detail_logger_fn(&remaining_pids);
        }
        crate::modules::logger::log_error(&format!(
            "[{}] still_running_entries={}",
            log_prefix,
            summarize_process_entries_for_log(&remaining_entries)
        ));
        return Err(format!(
            "{} ({})",
            failure_message,
            summarize_pid_list_for_log(&remaining_pids)
        ));
    }

    Ok(())
}

View on GitHub (pinned to 1ed8b77992)