GoogleContainerTools/skaffold · error

could not assign job object

Error message

could not assign job object: %w

What it means

Start's final setup step assigns the started process to the Windows job object via AssignProcessToJobObject so it is killed when the job closes; this error wraps failure of that assignment. The process runs but is no longer guaranteed to be terminated together with its children on shutdown.

Solutions

  1. Fix the child process exiting immediately (validate it runs standalone) so assignment races don't occur
  2. Update Windows / golang.org/x/sys to support nested job objects if the process is already in a job
  3. Run with sufficient privileges; check access-denied in the wrapped error
  4. Check AV/EDR interference; verify the parent's job object handle is still valid
Defensive patterns

Strategy: try-catch

Try / catch

if err := c.Start(); err != nil {
    if strings.Contains(err.Error(), "could not assign job object") {
        log.Printf("warning: child not in job object; may outlive parent: %v", err)
        // optionally Terminate manually
    }
    return err
}

Prevention

When it happens

Trigger: windows.AssignProcessToJobObject(handle, processHandle) returns an error — access denied opening/assigning, the process has already exited, or the process is already part of an incompatible job (nested job limits on older Windows).

Common situations: Child exiting before assignment (fast-crashing binaries), Windows versions restricting nested job assignment, restricted service tokens, security software interfering with process/job manipulation.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/042271dac21bef55. Report an issue: GitHub.

Appendix: source

Thrown at pkg/skaffold/kubectl/exec_windows.go:73

	if _, err := windows.SetInformationJobObject(
		handle,
		windows.JobObjectExtendedLimitInformation,
		uintptr(unsafe.Pointer(&info)),
		uint32(unsafe.Sizeof(info))); err != nil {
		return fmt.Errorf("could not set information job object: %w", err)
	}

	if err := c.Cmd.Start(); err != nil {
		return fmt.Errorf("could not start the command: %w", err)
	}

	processHandle, err := getHandleFromProcess(c.Process)
	if err != nil {
		return fmt.Errorf("could not get handle from process: %w", err)
	}

	if err := windows.AssignProcessToJobObject(handle, processHandle); err != nil {
		return fmt.Errorf("could not assign job object: %w", err)
	}

	c.handle = handle
	go func() {
		<-c.ctx.Done()
		c.Terminate()
	}()

	return nil
}

func getHandleFromProcess(p *os.Process) (windows.Handle, error) {
	// os.Process contains an unexported processHandle struct, which contains
	// a `handle uintptr` field.
	v := reflect.ValueOf(p)
	i := reflect.Indirect(v)

	k := i.Kind()

View on GitHub (pinned to a1189de023)