FiloSottile/age · error

expected ok or fail stanza, got %q

Error message

expected ok or fail stanza, got %q

What it means

readOkOrFail accepts only 'ok' or 'fail' as the reply to an interactive command; any other stanza type produces this error naming the unexpected type. The plugin responded with a stanza that does not fit the request/response contract for DisplayMessage, RequestValue, or Confirm.

Source

Thrown at plugin/plugin.go:654

	}
	return expectStanzaWithNoBody(ok, 0)
}

func readOkOrFail(sr *format.StanzaReader) (*format.Stanza, error) {
	s, err := sr.ReadStanza()
	if err != nil {
		return nil, fmt.Errorf("failed to read response stanza: %v", err)
	}
	switch s.Type {
	case "fail":
		if err := expectStanzaWithNoBody(s, 0); err != nil {
			return nil, fmt.Errorf("%v", err)
		}
		return s, nil
	case "ok":
		return s, nil
	default:
		return nil, fmt.Errorf("expected ok or fail stanza, got %q", s.Type)
	}
}

func expectUnsupported(sr *format.StanzaReader) error {
	unsupported, err := sr.ReadStanza()
	if err != nil {
		return fmt.Errorf("failed to read unsupported stanza: %v", err)
	}
	if unsupported.Type != "unsupported" {
		return fmt.Errorf("expected unsupported stanza, got %q", unsupported.Type)
	}
	return expectStanzaWithNoBody(unsupported, 0)
}

func (p *Plugin) writeError(args []string, err error) error {
	s := &format.Stanza{Type: "error", Args: args}
	s.Body = []byte(err.Error())
	if err := s.Marshal(p.stdout); err != nil {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Check which stanza type was received (it is quoted in the error) and map it to the plugin's behavior
  2. Confirm the plugin advertises support for the interactive command being issued
  3. Upgrade the plugin; if it cannot support the command it must reply 'fail', not an arbitrary stanza
  4. Ensure plugin diagnostics go to stderr so they never pollute the stanza stream

Example fix

// before (plugin side)
fmt.Printf("-> maybe\n\n")

// after
fmt.Printf("-> fail\n\n") // or "-> ok\n\n"
Defensive patterns

Strategy: type-guard

Type guard

func isOkOrFail(s *format.Stanza) bool {
    return s != nil && (s.Type == "ok" || s.Type == "fail")
}

Try / catch

s, err := readOkOrFail(sr)
if err != nil {
    if strings.HasPrefix(err.Error(), "expected ok or fail stanza") {
        return fmt.Errorf("plugin does not support this interactive command: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A plugin replies with a data stanza, 'unsupported', or a custom type where the client demanded 'ok'/'fail'; command sequencing desynchronized so a stale stanza from an earlier exchange is read here.

Common situations: Plugin not implementing the interactive-message protocol (responds 'unsupported' to a display-message/command); protocol version skew; plugin echoing input or diagnostics onto stdout that the reader interprets as a stanza.

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/16a5f09cc40e846a. Report an issue: GitHub.