JuliusBrussee/caveman · error

aes-gcm

Error message

aes-gcm: %w

What it means

cipher.NewGCM(block) wraps the AES block cipher in Galois/Counter Mode. In the Go standard library this call only fails if the underlying block's block size is not 16 bytes, which cannot happen with a successfully constructed aes.Block, so this error is effectively unreachable in practice. It is kept as a defensive wrap in case the block construction or crypto backend changes.

Solutions

  1. Verify the binary is built against the standard crypto/aes and crypto/cipher packages (check vendoring and replace directives in go.mod).
  2. Rebuild without custom crypto replacements: go build with a clean module graph.
  3. Check for any FIPS or custom cipher injection via build tags or init-time package replacement.
  4. If it persists on the standard toolchain, file an upstream Go issue with go version output.

Example fix

// before: go.mod replace directive swapping crypto internals
replace crypto/cipher => ./vendor/fips-cipher

// after: use standard library
delete the replace directive, then: go mod tidy && go build
Defensive patterns

Strategy: try-catch

Try / catch

out, err := secretbox.Encrypt(pt)
if err != nil {
    if strings.HasPrefix(err.Error(), "aes-gcm:") {
        // crypto backend invariant broken; escalate, not retryable
        log.Fatalf("crypto backend invariant violated: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Practically never with the standard crypto/aes block: it would require aes.NewCipher to succeed but return a block whose BlockSize() != 16, e.g. only conceivable with a custom or replaced crypto backend or unusual build (FIPS module mismatch).

Common situations: Almost only seen in exotic environments: patched/regulated crypto builds, vendored crypto replacements, or a bug report worth forwarding upstream. If you see it, the key was fine (it passed aes.NewCipher) and the failure is environmental.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/d18ec27e183a8100. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/secretbox/secretbox.go:76

		if err != nil {
			return nil, fmt.Errorf("secretbox: KMS encrypt: %w", err)
		}
		return wrapped, nil
	}
	if runtimeenv.IsProduction() {
		return nil, fmt.Errorf("secretbox: production requires CAVE_KMS_PROVIDER=scaleway")
	}
	keyBytes, err := loadKey()
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return nil, fmt.Errorf("aes cipher: %w", err)
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("aes-gcm: %w", err)
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := rand.Read(nonce); err != nil {
		return nil, fmt.Errorf("nonce entropy: %w", err)
	}
	// Seal appends the ciphertext+tag to nonce, so the returned slice is the
	// full nonce||ciphertext envelope.
	return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

// EncryptPayloadKey wraps an artifact data-encryption key. Production uses the
// dedicated payload KEK; local development retains the same AES-GCM envelope as
// other local secrets.
func EncryptPayloadKey(plaintext []byte) ([]byte, error) {
	if useKMS() {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		wrapped, err := kms.EncryptPayload(ctx, plaintext)

View on GitHub (pinned to 3ee70a1026)