sipeed/picoclaw · error

assign process to job object: %w

Error message

assign process to job object: %w

What it means

Thrown by the Windows isolation backend's post-start hook. After a child process is spawned with a restricted token, the runtime creates a job object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE), opens the child PID with PROCESS_SET_QUOTA|PROCESS_TERMINATE|PROCESS_QUERY_LIMITED_INFORMATION|SYNCHRONIZE, and calls AssignProcessToJobObject. If that Win32 call fails, isolation setup aborts, the job/process/token handles are closed, and this wrapped error (including the syscall.Errno) is returned.

Source

Thrown at pkg/isolation/platform_windows.go:114

		windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE|windows.PROCESS_QUERY_LIMITED_INFORMATION|windows.SYNCHRONIZE,
		false,
		uint32(cmd.Process.Pid),
	)
	if err != nil {
		_ = windows.CloseHandle(job)
		if resources.token != 0 {
			_ = resources.token.Close()
		}
		return fmt.Errorf("open process for job assignment: %w", err)
	}

	if err = windows.AssignProcessToJobObject(job, proc); err != nil {
		_ = windows.CloseHandle(proc)
		_ = windows.CloseHandle(job)
		if resources.token != 0 {
			_ = resources.token.Close()
		}
		return fmt.Errorf("assign process to job object: %w", err)
	}

	if resources.token != 0 {
		_ = resources.token.Close()
	}
	resources.job = job
	windowsProcessResourcesByPID.Store(cmd.Process.Pid, resources)
	go reapWindowsProcessResources(cmd.Process.Pid, proc, job)
	return nil
}

func cleanupPendingPlatformResources(cmd *exec.Cmd) {
	if cmd == nil {
		return
	}
	resourcesAny, ok := windowsPendingResources.LoadAndDelete(cmd)
	if !ok {
		return

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Inspect the wrapped errno (errors.As to syscall.Errno): ERROR_ACCESS_DENIED (5) points to job/permission confinement, ERROR_NOT_SUPPORTED (50) to nested-job limits on Windows 7
  2. Verify the child is still alive right after Start (check cmd.ProcessState / wait later); if it exits instantly, fix the command or path being launched
  3. Run the parent process outside already-restrictive job objects (avoid nesting the runtime inside CI/container jobs), or upgrade the host to Windows 8+ for nested job support
  4. If confinement is unavoidable, run with isolation disabled on that host (isolation.enabled=false) since the job-object guarantee cannot be established
  5. Reproduce manually with a minimal Go snippet calling AssignProcessToJobObject to confirm the host policy blocks it

Example fix

# before: launching an instantly-exiting command under isolation
isolation: {enabled: true}
command: ["nonexistent-tool"]  # child dies before job assignment

# after: verify the child survives, launch a real command
isolation: {enabled: true}
command: ["C:\\Tools\\tool.exe"]
Defensive patterns

Strategy: try-catch

Validate before calling

// before spawning isolated children on windows, detect a confining parent job
var inJob bool
if err := windows.IsProcessInJob(windows.CurrentProcess(), 0, &inJob); err == nil && inJob {
    // parent is already job-confined; nested assignment can fail — plan for it or refuse isolation here
    log.Warn("parent process is inside a job object; isolation assignment may fail")
}

Try / catch

if err := launchIsolated(cmd); err != nil {
    if strings.Contains(err.Error(), "assign process to job object") {
        var errno syscall.Errno
        if errors.As(err, &errno) {
            switch errno {
            case windows.ERROR_ACCESS_DENIED, windows.ERROR_NOT_SUPPORTED:
                // host policy prevents job confinement: kill the unrestricted child
                _ = cmd.Process.Kill()
                return fmt.Errorf("isolation unavailable on this host (errno %d); refusing to run unconfined", errno)
            }
        }
    }
    return err
}

Prevention

When it happens

Trigger: AssignProcessToJobObject failing: (1) the child exited between OpenProcess and assignment (fast-failing command, bad executable path); (2) the child is already in another job that does not allow breakaway/nesting — nested jobs require Windows 8+, so a Windows 7 parent already in a job fails with ERROR_NOT_SUPPORTED / ERROR_ACCESS_DENIED; (3) the parent itself runs inside a restrictive job or sandbox (CI runner, Docker Desktop, EDR) that denies PROCESS_SET_QUOTA-style operations; (4) security software blocking handle operations on the child.

Common situations: Running picoclaw's isolation mode inside CI agents or container sandables that already confine processes to jobs; launching very short-lived children (the process is gone before it can be attached); Windows 7/Server 2008 R2 hosts where a child can belong to only one job; antivirus interference with job assignment.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/6ff76a37304dce96. Report an issue: GitHub.