github/copilot-sdk · error

CLI exited before Job Object assignment

Error message

CLI exited before Job Object assignment

What it means

On Windows, attach_and_resume spawns the CLI suspended and must assign it to a Job Object before resuming it. child.raw_handle() returns None if the process already exited while suspended, so the code raises NotFound with this message instead of passing a null handle into unsafe Win32 APIs.

Solutions

  1. Verify the CLI executable path and that it runs standalone (check for missing DLLs/errors)
  2. Check Windows Event Viewer / stderr for the process's actual exit reason
  3. Exclude the CLI from antivirus/EDR interference or whitelist it
  4. Confirm CLI version compatibility with the SDK

Example fix

// before
let child = Command::new(cli_path).spawn()?; // may exit instantly
// after
let cli_path = which("copilot").expect("CLI not found; install or fix PATH");
let child = Command::new(cli_path).spawn()?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust (before spawning)
let cli_path = which::which("copilot")?; // fail fast if CLI missing
std::process::Command::new(&cli_path).arg("--version").status()?; // confirm it runs

Try / catch

// Rust
match process_tree::spawn(cmd) {
    Err(e) if e.kind() == io::ErrorKind::NotFound => eprintln!("CLI died at spawn: check install/AV logs"),
    r => r?,
}

Prevention

When it happens

Trigger: The spawned CLI process terminates between CreateProcess (suspended) and the AssignProcessToJobObject step — bad executable, missing DLLs, instant crash, or an antivirus killing the process.

Common situations: Wrong CLI path or corrupted install on Windows; missing runtime dependencies (e.g. WebView2/VC runtime); security software terminating the newly spawned process; incompatible CLI version.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/10004425df135a41. Report an issue: GitHub.

Appendix: source

Thrown at rust/src/process_tree.rs:129

        let job = OwnedHandle(raw_job);

        let mut limits = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
        limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        // SAFETY: `limits` has the layout required by the selected info class.
        if unsafe {
            SetInformationJobObject(
                job.0,
                JobObjectExtendedLimitInformation,
                ptr::from_ref(&limits).cast(),
                size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
            )
        } == 0
        {
            return Err(io::Error::last_os_error());
        }

        let process = child.raw_handle().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::NotFound,
                "CLI exited before Job Object assignment",
            )
        })?;
        // SAFETY: both handles are valid and the child is still suspended.
        if unsafe { AssignProcessToJobObject(job.0, process.cast()) } == 0 {
            return Err(io::Error::last_os_error());
        }

        resume_initial_thread(child.id().ok_or_else(|| {
            io::Error::new(io::ErrorKind::NotFound, "CLI exited before thread resume")
        })?)?;
        Ok(Tree { job })
    }

    fn resume_initial_thread(pid: u32) -> io::Result<()> {
        // SAFETY: the returned snapshot handle is owned and closed below.
        let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) };

View on GitHub (pinned to cd8cf15dc3)