FiloSottile/age · error

expected OK stanza, got %q

Error message

expected OK stanza, got %q

What it means

The client successfully read a stanza but its Type was not 'ok'. expectOk is used after commands that must be acknowledged with a bare 'ok' stanza (recipient/identity wrap, error reporting). Receiving any other stanza type means the plugin responded out of protocol order or with an unexpected reply.

Source

Thrown at plugin/plugin.go:635

		return p.fatalf("%v", err)
	}
	return 3
}

func (p *Plugin) identityError(idx int, err error) int {
	if err := p.writeError([]string{"identity", fmt.Sprint(idx)}, err); err != nil {
		return p.fatalf("%v", err)
	}
	return 3
}

func expectOk(sr *format.StanzaReader) error {
	ok, err := sr.ReadStanza()
	if err != nil {
		return fmt.Errorf("failed to read OK stanza: %v", err)
	}
	if ok.Type != "ok" {
		return fmt.Errorf("expected OK stanza, got %q", ok.Type)
	}
	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:

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Log the received stanza type to see what the plugin actually replied
  2. Confirm the plugin supports the command that was just sent (wrap/unwrap recipient-v1/identity-v1)
  3. Upgrade the plugin to a protocol-conformant version
  4. Ensure the plugin never writes human-readable text to stdout (stdout is protocol-only; use stderr)

Example fix

// before (plugin side)
fmt.Printf("cannot handle this recipient\n\n") // parsed as a stanza

// after
fmt.Fprintf(os.Stderr, "cannot handle this recipient\n")
fmt.Printf("-> fail\n\n")
Defensive patterns

Strategy: type-guard

Type guard

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

Try / catch

if err := expectOk(sr); err != nil {
    var unexpected *unexpectedStanzaError
    if errors.As(err, &unexpected) {
        log.Printf("plugin replied %q; check plugin protocol support", unexpected.Type)
    }
    return err
}

Prevention

When it happens

Trigger: After writeError sends an 'error' stanza, the plugin replies with something other than 'ok'; a plugin replies 'fail' or 'unsupported' where 'ok' is mandated; the client and plugin disagree on which command is in flight, so the reply is the answer to a previous command.

Common situations: Plugin does not implement the full plugin protocol (e.g. no support for the unsupported/error handshake); version mismatch causing off-by-one command sequencing; plugin echoing diagnostics onto stdout that get parsed as a stanza line.

Related errors


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