FiloSottile/age · error

failed to read unsupported stanza

Error message

failed to read unsupported stanza: %v

What it means

expectUnsupported is used when the client probes whether the plugin supports a command (unsupported-stanza handshake in RecipientV1/IdentityV1). A ReadStanza failure here — EOF, broken pipe, or bad framing — is wrapped with this message. It means the plugin never delivered the 'unsupported' acknowledgment the probe expected.

Solutions

  1. Verify the plugin binary runs standalone and does not crash on the probe command
  2. Check the plugin's stderr output for a panic or early exit
  3. Confirm the plugin implements the command-unsupported reply of the plugin protocol
  4. Reinstall/upgrade the plugin and check the AGE_PLUGIN path points to the right binary

Example fix

// before (plugin side) — exits without replying to unknown command
if cmd != "recipient-v1" { os.Exit(1) }

// after
if cmd != "recipient-v1" { fmt.Printf("-> unsupported\n\n"); return }
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(pluginPath); err != nil {
    return fmt.Errorf("plugin binary %q not found: %w", pluginPath, err)
}
if err := exec.Command(pluginPath).Run(); err != nil {
    return fmt.Errorf("plugin binary not executable: %w", err)
}

Try / catch

if err := expectUnsupported(sr); err != nil {
    if strings.Contains(err.Error(), "failed to read unsupported stanza") {
        return fmt.Errorf("plugin %s crashed during capability probe: %w (stderr: %s)", pluginName, err, stderrBuf.String())
    }
    return err
}

Prevention

When it happens

Trigger: During RecipientV1/IdentityV1 negotiation the client reads for an 'unsupported' reply; the plugin exits, crashes, or emits a stanza with invalid framing so ReadStanza errors before any 'unsupported' arrives.

Common situations: Plugin binary missing or failing to exec (though that usually fails earlier, at pipe setup); plugin panics on the probing command; plugin speaks a protocol without the unsupported handshake and just terminates; environment issues like a non-executable or wrong-architecture plugin binary.

Related errors


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

Appendix: source

Thrown at plugin/plugin.go:661

		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 {
		return fmt.Errorf("failed to write error stanza: %v", err)
	}
	if err := expectOk(p.sr); err != nil {
		return fmt.Errorf("%v", err)
	}
	return nil
}

View on GitHub (pinned to b74dce4cdb)