FiloSottile/age · error

received duplicated file-key stanza

Error message

received duplicated file-key stanza

What it means

The plugin sent a second file-key stanza within one unwrap session. The client already received and acknowledged one file key; the protocol allows only one per invocation, so a duplicate indicates a buggy plugin that is answering the same request twice.

Source

Thrown at plugin/client.go:281

		if err != nil {
			return nil, err
		}

		switch s.Type {
		case "file-key":
			if len(s.Args) != 1 {
				return nil, fmt.Errorf("malformed file-key stanza: unexpected arguments count")
			}
			n, err := strconv.Atoi(s.Args[0])
			if err != nil {
				return nil, fmt.Errorf("malformed file-key stanza: invalid index")
			}
			// We only send a single file key, so the index must be 0.
			if n != 0 {
				return nil, fmt.Errorf("malformed file-key stanza: unexpected index")
			}
			if fileKey != nil {
				return nil, fmt.Errorf("received duplicated file-key stanza")
			}

			fileKey = s.Body

			if err := writeStanza(conn, "ok"); err != nil {
				return nil, err
			}
		case "error":
			if err := writeStanza(conn, "ok"); err != nil {
				return nil, err
			}

			return nil, fmt.Errorf("%s", s.Body)
		case "done":
			break ReadLoop
		default:
			if ok, err := i.ui.handle(i.name, conn, s); err != nil {
				return nil, err

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Update the plugin to send the file-key stanza exactly once per session, then wait for "done"
  2. Audit the plugin's state machine: after replying to file-key it should only send "error" or "done"
  3. Test the plugin against age-plugin-test-tool or the official age client to catch duplicate stanzas

Example fix

// before (plugin side)
for { handleStanza(); if isFileKey { sendFileKey() } } // loops forever
// after
handleFileKeyRequest(); sendFileKey(); awaitDone()
Defensive patterns

Strategy: validation

Validate before calling

// In plugin code, assert single-send before writing file-key
if sent { t.Fatal("file-key sent twice") }; sent = true

Try / catch

err := identity.Unwrap(...)
if err != nil && strings.Contains(err.Error(), "duplicated file-key") {
    return fmt.Errorf("plugin %s sent file-key twice; plugin state machine bug", pluginName)
}

Prevention

When it happens

Trigger: During Unwrap, the plugin writes "file-key" again after the client has already stored fileKey and replied with an "ok" stanza.

Common situations: Plugin loops that don't break after answering the file-key request; plugin handling both wrap and unwrap paths incorrectly; a plugin that retries internally after a slow "ok" acknowledgment.

Related errors


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