gastownhall/beads · error

positional argument %q contains '=', which no issue id does

Error message

positional argument %q contains '=', which no issue id does — this is a mis-typed flag value; repeat the flag per pair, e.g. --set-metadata a=1 --set-metadata b=2

What it means

bd update rejects any positional argument containing '=' because issue IDs never contain '='; such an argument is unambiguously a flag value that failed to bind to its flag. Rejecting it up front prevents a partial write that would apply only some of the intended metadata pairs (bd-5247).

Source

Thrown at cmd/bd/update.go:850

// updateIDFailure records one issue ID that could not be updated and why.
// GuardMismatch marks a --if-assignee/--if-status refusal so JSON consumers
// can distinguish it without parsing the error text.
type updateIDFailure struct {
	ID            string `json:"id"`
	Error         string `json:"error"`
	GuardMismatch bool   `json:"guard_mismatch,omitempty"`
}

// errStrayFlagValuePositional refuses, before any write, a positional argument
// that contains '='. --set-metadata takes ONE key=value per flag, so
// `--set-metadata a=1 b=2` silently turns `b=2` into a positional issue id;
// no issue id contains '=', so such a positional is unambiguously a mis-typed
// flag value. Rejecting it up front prevents a partial write that would apply
// only the pairs that happened to bind to a flag (bd-5247).
func errStrayFlagValuePositional(args []string) error {
	for _, arg := range args {
		if strings.Contains(arg, "=") {
			return fmt.Errorf("positional argument %q contains '=', which no issue id does — this is a mis-typed flag value; repeat the flag per pair, e.g. --set-metadata a=1 --set-metadata b=2", arg)
		}
	}
	return nil
}

// reportUpdateFailures emits a per-ID failure report on stderr and returns a
// nonzero exit error — ExitGuardMismatch when every failure is a
// --if-assignee/--if-status guard refusal, 1 otherwise. In --json mode the
// report is a single compact JSON line — the last line on stderr — so
// callers can parse which IDs failed while stdout keeps the plain
// array-of-updated-issues success shape. In text mode the individual errors
// were already printed inline; this adds a summary naming every failed ID.
func reportUpdateFailures(failures []updateIDFailure, total int) error {
	msg := fmt.Sprintf("%d of %d issues failed to update", len(failures), total)
	if jsonOutput {
		inner := map[string]interface{}{
			"error":  msg,
			"failed": failures,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Repeat the flag per pair: --set-metadata a=1 --set-metadata b=2
  2. Remove the stray positional or bind it to the intended flag
  3. Re-run with only the issue ID as the positional argument

Example fix

// before
bd update bd-42 --set-metadata a=1 b=2
// after
bd update bd-42 --set-metadata a=1 --set-metadata b=2
Defensive patterns

Strategy: validation

Validate before calling

args := cmd.Flags().Args()
for _, a := range args {
    if strings.Contains(a, "=") {
        return fmt.Errorf("positional %q looks like an unbound flag value; repeat the flag per pair", a)
    }
}

Prevention

When it happens

Trigger: Invoking `bd update bd-1 --set-metadata a=1 b=2` — only the first pair binds to --set-metadata (cobra consumes one value per occurrence), leaving `b=2` as a stray positional, which errStrayFlagValuePositional detects.

Common situations: Users assuming --set-metadata accepts multiple pairs in one value or as trailing variadic args; scripts converted from other tools' `--meta k1=v1 k2=v2` style; copy-pasted commands with missing repeated flag names.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/f72aa58e503a08b9. Report an issue: GitHub.