ory/hydra · error

newWorker: failed to create stdin pipe

Error message

newWorker: failed to create stdin pipe

What it means

`newWorker` builds the child `exec.Cmd` for the jsonnet worker process; `cmd.StdinPipe()` failed, so no stdin pipe could be created and the worker construction aborts. In practice this almost always means the process is out of file descriptors, since pipe creation requires two fds.

Source

Thrown at oryx/jsonnetsecure/jsonnet_pool.go:136

func newWorker(ctx context.Context) (_ worker, err error) {
	tracer := trace.SpanFromContext(ctx).TracerProvider().Tracer("")
	ctx, span := tracer.Start(ctx, "jsonnetsecure.newWorker")
	defer otelx.End(span, &err)

	path, _ := ctx.Value(contextValuePath).(string)
	if path == "" {
		return worker{}, errors.New("newWorker: missing binary path in context")
	}
	args, _ := ctx.Value(contextValueArgs).([]string)
	cmd := exec.Command(path, append(args, "-0")...)
	cmd.Env = []string{"GOMAXPROCS=1"}
	cmd.WaitDelay = 100 * time.Millisecond

	span.SetAttributes(semconv.ProcessCommand(cmd.Path), semconv.ProcessCommandArgs(cmd.Args...))

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return worker{}, errors.Wrap(err, "newWorker: failed to create stdin pipe")
	}

	in := make(chan []byte, 1)
	go func(c <-chan []byte) {
		for input := range c {
			if _, err := stdin.Write(append(input, 0)); err != nil {
				stdin.Close()
				return
			}
		}
	}(in)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return worker{}, errors.Wrap(err, "newWorker: failed to create stdout pipe")
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Check fd usage (`lsof -p <pid> | wc -l`) and raise the limit (`ulimit -n`, systemd LimitNOFILE, container ulimits)
  2. Reduce the jsonnet worker pool size — NewProcessPool enforces a minimum of 5, so very small containers may need a bigger fd budget
  3. Look for fd leaks: worker processes that are never destroyed keep pipes open; ensure the pool is Closed on shutdown
  4. Restart the process to clear leaked fds if the pool has been running a long time

Example fix

// before: large pool on a box with low ulimit
pool := jsonnetsecure.NewProcessPool(100) // needs ~200+ fds
// after: size the pool to the environment
pool := jsonnetsecure.NewProcessPool(runtime.NumCPU()) // and raise ulimit -n accordingly
Defensive patterns

Strategy: validation

Validate before calling

// check fd headroom before creating the pool
fds, _ := filepath.Glob("/proc/self/fd/*")
var lim syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
if len(fds) > int(lim.Cur)-2*poolSize {
    return errors.New("not enough file descriptors for worker pool")
}

Prevention

When it happens

Trigger: Called by the puddle pool constructor when spinning up a worker: `cmd.StdinPipe()` returns an error — overwhelmingly `EMFILE`/`ENFILE` (fd limit reached) from too many open files/sockets in the parent process.

Common situations: Large worker pool sizes combined with many open connections/files exhausting the fd limit; leaking worker processes/pipes over time so each new worker needs fds that no longer exist; low `ulimit -n` in containers.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/903e84d253387a0f. Report an issue: GitHub.