slackhq/nebula · error

-%s and -%s both set to %q, only one output may write to std

Error message

-%s and -%s both set to %q, only one output may write to stdout

What it means

Symmetric to claimIn: only one output flag may write to stdout. ioClaims.out records the flag that claimed stdout; claimOut errors when a second, different output flag also requests '-', because two outputs cannot share the same stream without interleaving corruption.

Source

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

// 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 {
	return reserveStdio(claims, "reserveInputs", (*ioClaims).claimIn, pairs)
}

// reserveOutputs walks alternating (flagName, path) pairs and claims stdout
// for any path equal to stdioPath. It must be called before any output is
// written so a conflict cannot leave one stream half written before the
// second flag fails.
func reserveOutputs(claims *ioClaims, pairs ...string) error {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Set only ONE output flag to '-' and give other outputs real file paths
  2. Omit optional outputs like -out-qr when writing the cert to stdout
  3. Fix variables in wrapper scripts so paths are not blank

Example fix

// before
nebula-cert sign -ca ca.pem -key ca.key -name host -out-crt - -out-qr -
// after
nebula-cert sign -ca ca.pem -key ca.key -name host -out-crt -
Defensive patterns

Strategy: validation

Validate before calling

func validateOutputs(paths map[string]string) error {
    n := 0
    for _, p := range paths {
        if p == "-" { n++ }
    }
    if n > 1 {
        return fmt.Errorf("%d output flags write stdout; 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 output may write to stdout") {
    log.Printf("fix stdout flags: %s", out)
}

Prevention

When it happens

Trigger: claimOut is called with a flag name different from the one already holding c.out — e.g. both -out-crt and -out-qr set to '-' during signCert.

Common situations: User passes '-' for multiple output flags expecting both to print; scripts with unset path variables defaulting to '-'.

Related errors


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