slackhq/nebula · error

-%s and -%s both set to %q, only one input may read from std

Error message

-%s and -%s both set to %q, only one input may read from stdin

What it means

nebula-cert commands allow exactly one input flag to read from stdin ('-'). The ioClaims helper tracks which flag claimed stdin; claimIn returns this error when a second, different flag also requests stdin, since two inputs cannot both read the same stream.

Source

Thrown at cmd/nebula-cert/stdio.go:32

// help so the - convention is documented once instead of on every flag.
const stdioHelpText = "  Pass \"-\" to any path flag to read from stdin or write to stdout.\n"

// stdinReader is the source used when an input flag is set to "-".
// It is a package level var so tests can swap in a deterministic reader.
// Tests that mutate stdinReader cannot run with t.Parallel().
var stdinReader io.Reader = os.Stdin

// ioClaims tracks which flags have claimed stdin and stdout during a single
// command invocation so we can refuse a second flag asking for the same
// stream.
type ioClaims struct {
	in  string
	out string
}

func (c *ioClaims) claimIn(flagName string) error {
	if c.in != "" && c.in != flagName {
		return fmt.Errorf("-%s and -%s both set to %q, only one input may read from stdin", c.in, flagName, stdioPath)
	}
	c.in = flagName
	return nil
}

func (c *ioClaims) claimOut(flagName string) error {
	if c.out != "" && c.out != flagName {
		return fmt.Errorf("-%s and -%s both set to %q, only one output may write to stdout", c.out, flagName, stdioPath)
	}
	c.out = flagName
	return nil
}

// reserveInputs walks alternating (flagName, path) pairs and claims stdin
// for any path equal to stdioPath. It must be called before any input is
// read so a conflict can be reported immediately instead of blocking on
// io.ReadAll while waiting for input that will never arrive.
func reserveInputs(claims *ioClaims, pairs ...string) error {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Set only ONE input flag to '-' and give the others real file paths
  2. Concatenate PEM material (e.g. CA then key) into a single stdin stream only if the command supports it, otherwise use files
  3. Fix script variables so paths aren't blank, which can default to '-'

Example fix

// before
nebula-cert sign -ca - -key - -name host ...
// after
nebula-cert sign -ca ca.pem -key - -name host ... < ca.key
Defensive patterns

Strategy: validation

Validate before calling

func validateInputs(paths map[string]string) error {
    n := 0
    for name, p := range paths {
        if p == "-" { n++ }
        _ = name
    }
    if n > 1 {
        return fmt.Errorf("%d input flags read stdin; only one allowed", n)
    }
    return nil
}

Try / catch

out, err := exec.Command("nebula-cert", args...).CombinedOutput()
if err != nil && strings.Contains(string(out), "only one input may read from stdin") {
    log.Printf("fix stdin flags: %s", out)
}

Prevention

When it happens

Trigger: Calling claimIn (via openInput/readInput during sign/verify etc.) with a flag name different from the one that already claimed stdin — i.e. two input flags (e.g. -ca and -key, or -ca and -crt) are both set to '-'.

Common situations: User passes '-' for multiple input paths, e.g. -ca - -key -, intending to pipe both; script templating substituting empty paths with '-'.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/4e8349d64bdfd963. Report an issue: GitHub.