getsops/sops · error

failed to copy age decrypted data into bytes.Buffer: %w

Error message

failed to copy age decrypted data into bytes.Buffer: %w

What it means

sops failed to read the plaintext output produced by the age decryption stream into memory. After a successful age.Decrypt call, the resulting reader r is copied into a bytes.Buffer; an error here means the underlying reader (e.g. the armored/file stream wrapping the data key) returned an I/O error mid-read, so decryption is aborted. This indicates the decrypted stream was corrupt, truncated, or unreadable, not that key decryption itself failed.

Source

Thrown at age/keysource.go:267

		if len(ids) == 0 {
			log.Info("Decryption failed")
			return nil, formatError("failed to load age identities", nil, errs, unusedLocations)
		}
		ids.ApplyToMasterKey(key)
	}

	src := bytes.NewReader([]byte(key.EncryptedKey))
	ar := armor.NewReader(src)
	r, err := age.Decrypt(ar, key.parsedIdentities...)
	if err != nil {
		log.Info("Decryption failed")
		return nil, formatError("failed to create reader for decrypting sops data key with age", err, errs, unusedLocations)
	}

	var b bytes.Buffer
	if _, err := io.Copy(&b, r); err != nil {
		log.Info("Decryption failed")
		return nil, fmt.Errorf("failed to copy age decrypted data into bytes.Buffer: %w", err)
	}

	log.Info("Decryption succeeded")
	return b.Bytes(), nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return false
}

// ToString converts the key to a string representation.
func (key *MasterKey) ToString() string {
	return key.Recipient
}

// ToMap converts the MasterKey to a map for serialization purposes.
func (key *MasterKey) ToMap() map[string]interface{} {

View on GitHub (pinned to 13442bb981)

Solutions

  1. Verify the encrypted file is complete and unmodified (git checkout the original file, re-download, or re-encrypt from source).
  2. Re-run sops with SOPS_AGE_DEBUG or general logging to see which key/reader errored.
  3. If using an age plugin recipient, confirm the plugin binary is installed, executable, and works with a direct age round-trip.
  4. Check disk space and file permissions on the input file; retry the decrypt.
  5. Regenerate the data key by re-encrypting the file with a known-good age identity.

Example fix

// before: decrypting a truncated file pulled from a partial sync
sops -d secrets.enc.yaml
// failed to copy age decrypted data into bytes.Buffer: unexpected EOF

// after: restore the complete file, then decrypt
git checkout -- secrets.enc.yaml
sops -d secrets.enc.yaml
Defensive patterns

Strategy: try-catch

Validate before calling

// Go caller
if info, err := os.Stat(encFile); err != nil || info.Size() == 0 {
    return fmt.Errorf("encrypted file missing or empty: %s", encFile)
}
// verify round-trip once at deploy time:
// sops -d encFile > /dev/null && echo ok

Try / catch

plaintext, err := key.Decrypt()
if err != nil {
    var agg *sopskey.ErrDecrypt  // or use errors.Is/As on the wrapped cause
    if errors.As(err, &agg) {
        log.Printf("age decrypt failed (corrupt/truncated stream?): %v", err)
        return fmt.Errorf("re-encrypt from source or restore file: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: MasterKey.Decrypt (age/keysource.go) calls io.Copy into a bytes.Buffer and the underlying reader returns an error before EOF: corrupted or truncated sops file, an age plugin binary that exits early, or a reader that errors after partial output.

Common situations: Sops-encrypted files damaged by partial writes or bad git merges; age plugin binaries (age-plugin-yubikey etc.) crashing or emitting garbage; piping truncated input into sops decrypt; filesystem/permission problems surfacing as read errors mid-stream.

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/b8cb98dff67cf195. Report an issue: GitHub.