{"record":{"id":"e26a4922140e22ac","repo":"JuliusBrussee/caveman","slug":"envelope-ciphertext-too-short","errorCode":null,"errorMessage":"envelope: ciphertext too short","messagePattern":"envelope: ciphertext too short","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/envelope/envelope.go","lineNumber":158,"sourceCode":"\twrapped, err := base64.StdEncoding.DecodeString(meta.WrappedDataKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: decode wrapped key: %w\", err)\n\t}\n\tdataKey, err := secretbox.DecryptPayloadKey(wrapped)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: unwrap data key: %w\", err)\n\t}\n\tblock, err := aes.NewCipher(dataKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: aes: %w\", err)\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: gcm: %w\", err)\n\t}\n\tns := gcm.NonceSize()\n\tif len(ciphertext) < ns {\n\t\treturn nil, fmt.Errorf(\"envelope: ciphertext too short\")\n\t}\n\tnonce, ct := ciphertext[:ns], ciphertext[ns:]\n\tplaintext, err := gcm.Open(nil, nonce, ct, aad)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: open: %w\", err)\n\t}\n\treturn plaintext, nil\n}\n\nfunc scopeAAD(scope Scope) ([]byte, string, error) {\n\tscope.OrganizationID = strings.TrimSpace(scope.OrganizationID)\n\tscope.ProjectID = strings.TrimSpace(scope.ProjectID)\n\tscope.Kind = strings.TrimSpace(scope.Kind)\n\tif scope.OrganizationID == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"envelope: organization scope is required\")\n\t}\n\tif scope.Kind == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"envelope: object kind is required\")","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/envelope/envelope.go#L140-L176","documentation":"The ciphertext blob passed to envelope decryption is shorter than the GCM nonce size (12 bytes for AES-GCM), so there is not even a nonce present. The library rejects it before attempting authenticated decryption. This almost always means the input is truncated, empty, corrupted, or not actually an envelope ciphertext.","triggerScenarios":"Calling envelope.Open or envelope.OpenForScope with a ciphertext byte slice of length less than the GCM nonce size (12) — e.g. an empty slice, a truncated stored blob, or plaintext handed in by mistake.","commonSituations":"A database row or object-store blob was truncated by a bad write; a column sized too small silently cut the value; decoding a base64 blob produced empty/partial bytes; passing a hash or ID string instead of the ciphertext.","solutions":["Check the length of the ciphertext before calling Open — it must be at least 12 bytes (nonce) plus 16 bytes (tag) plus plaintext","Re-fetch or restore the original encrypted blob; the stored copy is corrupted or truncated","Verify the write path that persisted the ciphertext completed without truncation (column size, streaming cutoffs)","Confirm you are passing the ciphertext, not plaintext or an encoded ID, into Open/OpenForScope"],"exampleFix":"// before\nplaintext, err := envelope.Open(ctx, keyRef, blobFromCache, nil)\n// after\nif len(blob) < 12+16 {\n    return fmt.Errorf(\"stored blob truncated: %d bytes\", len(blob))\n}\nplaintext, err := envelope.Open(ctx, keyRef, blob, nil)","handlingStrategy":"validation","validationCode":"if len(ciphertext) < 12+16 {\n    return fmt.Errorf(\"ciphertext too short (%d bytes): blob is truncated or not envelope data\", len(ciphertext))\n}","typeGuard":"func isPlausibleCiphertext(b []byte) bool { return len(b) >= 12+16 }","tryCatchPattern":"pt, err := envelope.Open(ctx, keyRef, ct, aad)\nif err != nil {\n    if strings.Contains(err.Error(), \"ciphertext too short\") {\n        // treat as corrupted/unrecoverable record; re-seal from source or surface 404\n    }\n    return err\n}","preventionTips":["Use large-enough DB columns / no truncating encodings for ciphertext blobs","Validate base64 decodes round-trip before storing","Never write plaintext into fields expected to hold ciphertext"],"tags":["go","crypto","aes-gcm","truncated-input"],"backgroundTag":"empty-required-field","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}