FiloSottile/age · warning

client failed to display message

Error message

client failed to display message

What it means

DisplayMessage is used by the plugin to show a message to the user via the age client's display-message protocol command. This error means the client answered the ok-or-fail exchange with a "fail" stanza — the client could not or refused to display the message. The plugin surfaces it as a normal error rather than a fatal interaction failure.

Source

Thrown at plugin/plugin.go:512

	return 0
}

// DisplayMessage requests that the client display a message to the user. The
// message should start with a lowercase letter and have no final period.
// DisplayMessage returns an error if the client can't display the message, and
// may return before the message has been displayed to the user.
//
// It must only be called by a Wrap or Unwrap method invoked by [Plugin.Main].
func (p *Plugin) DisplayMessage(message string) error {
	if err := writeStanzaWithBody(p.stdout, "msg", []byte(message)); err != nil {
		return p.fatalInteractf("failed to write msg stanza: %v", err)
	}
	s, err := readOkOrFail(p.sr)
	if err != nil {
		return p.fatalInteractf("%v", err)
	}
	if s.Type == "fail" {
		return fmt.Errorf("client failed to display message")
	}
	if err := expectStanzaWithNoBody(s, 0); err != nil {
		return p.fatalInteractf("%v", err)
	}
	return nil
}

// RequestValue requests a secret or public input from the user through the
// client, with the provided prompt. It returns an error if the client can't
// request the input or if the user dismisses the prompt.
//
// It must only be called by a Wrap or Unwrap method invoked by [Plugin.Main].
func (p *Plugin) RequestValue(prompt string, secret bool) (string, error) {
	t := "request-public"
	if secret {
		t = "request-secret"
	}
	if err := writeStanzaWithBody(p.stdout, t, []byte(prompt)); err != nil {

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Treat the display as best-effort and continue without it, or surface the message through stderr instead
  2. Run age in an environment where the client can display messages
  3. Upgrade the age client to a version supporting the display-message plugin command

Example fix

// before
if err := p.DisplayMessage("confirm device presence"); err != nil { return err }
// after
if err := p.DisplayMessage("confirm device presence"); err != nil {
    fmt.Fprintf(os.Stderr, "note: %s\n", "confirm device presence") // continue
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer running age interactively; check TTY before non-interactive flows:
// stat, _ := os.Stdin.Stat(); interactive := stat.Mode()&os.ModeCharDevice != 0

Try / catch

if err := p.DisplayMessage(msg); err != nil {
    // non-fatal: log to stderr and continue
    fmt.Fprintf(os.Stderr, "display skipped: %v\n", err)
}

Prevention

When it happens

Trigger: The age client running the plugin does not support display-message or its UI has no way to show messages (e.g. headless/batch mode); the client explicitly fails the request.

Common situations: Running age in a non-interactive environment (CI, scripts) where the client cannot show GUI/dialog messages; an old age client version lacking the display-message command; a client UI that drops the message.

Related errors


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