FiloSottile/age · error

%v

Error message

%v

What it means

fatalInteractf formats the underlying interaction error, writes it to the plugin's stderr, sets the plugin's broken flag so subsequent Wrap/Unwrap calls exit with an error, and returns the formatted error. The message text is simply the wrapped interaction protocol error, so the real cause is the format arguments (e.g. a malformed stanza or read failure).

Source

Thrown at plugin/plugin.go:580

	s, err = readOkOrFail(p.sr)
	if err != nil {
		return false, p.fatalInteractf("%v", err)
	}
	if s.Type == "fail" {
		return false, fmt.Errorf("client failed to request confirmation")
	}
	if err := expectStanzaWithNoBody(s, 1); err != nil {
		return false, p.fatalInteractf("%v", err)
	}
	return s.Args[0] == "yes", nil
}

// fatalInteractf prints the error to stderr and sets the broken flag, so the
// Wrap/Unwrap caller can exit with an error.
func (p *Plugin) fatalInteractf(format string, args ...any) error {
	p.broken = true
	fmt.Fprintf(p.stderr, format, args...)
	return fmt.Errorf(format, args...)
}

func (p *Plugin) fatalf(format string, args ...any) int {
	fmt.Fprintf(p.stderr, format, args...)
	return 1
}

func expectStanzaWithNoBody(s *format.Stanza, wantArgs int) error {
	if len(s.Args) != wantArgs {
		return fmt.Errorf("%s stanza has %d arguments, want %d", s.Type, len(s.Args), wantArgs)
	}
	if len(s.Body) != 0 {
		return fmt.Errorf("%s stanza has %d bytes of body, want 0", s.Type, len(s.Body))
	}
	return nil
}

func expectStanzaWithBody(s *format.Stanza, wantArgs int) error {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Inspect the stderr output from the plugin — fatalInteractf prints the real underlying message there
  2. Verify the age client and plugin versions both implement the same plugin protocol version
  3. Run without output redirection to ensure the plugin's stdin/stdout protocol channel is intact
Defensive patterns

Strategy: try-catch

Validate before calling

// Before interaction, ensure the protocol pipes are unmodified:
// (in plugin main) if os.Stdout has been redirected away from the client, abort early

Try / catch

if err != nil {
    // fatalInteractf already printed the cause to the plugin's stderr;
    // log err and mark the plugin broken so Wrap/Unwrap exits cleanly
    return err
}

Prevention

When it happens

Trigger: DisplayMessage, RequestValue, or Confirm reads a malformed/unexpected stanza from the client and calls fatalInteractf; readOkOrFail fails on a protocol violation; the client connection breaks mid-interaction.

Common situations: A buggy or too-old age client speaking a broken plugin protocol; the plugin's stdout/stderr being redirected causing protocol desync; corrupted interaction stream.

Related errors


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