{"record":{"id":"ca101ff0299ca94c","repo":"getsops/sops","slug":"decrypted-plaintext-data-cannot-be-cast-to-string","errorCode":null,"errorMessage":"decrypted plaintext data cannot be cast to string","messagePattern":"decrypted plaintext data cannot be cast to string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hcvault/keysource.go","lineNumber":386,"sourceCode":"func decryptPayload(encryptedKey string) map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"ciphertext\": encryptedKey,\n\t}\n}\n\n// dataKeyFromSecret attempts to extract the data key from the data of the\n// provided secret.\nfunc dataKeyFromSecret(secret *api.Secret) ([]byte, error) {\n\tif secret == nil || secret.Data == nil {\n\t\treturn nil, fmt.Errorf(\"transit backend is empty\")\n\t}\n\tdecrypted, ok := secret.Data[\"plaintext\"]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no decrypted data\")\n\t}\n\tplaintext, ok := decrypted.(string)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"decrypted plaintext data cannot be cast to string\")\n\t}\n\tdataKey, err := base64.StdEncoding.DecodeString(plaintext)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot decode base64 plaintext into data key bytes\")\n\t}\n\treturn dataKey, nil\n}\n\n// vaultClient returns a new Vault client, configured with the given address\n// and token.\nfunc vaultClient(address, token string, hc *http.Client) (*api.Client, error) {\n\tcfg := api.DefaultConfig()\n\tcfg.Address = address\n\n\tallowlist, err := getAllowlist()\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/hcvault/keysource.go#L368-L404","documentation":"This error is returned by dataKeyFromSecret when secret.Data[\"plaintext\"] exists but is not a Go string. Vault's transit decrypt returns plaintext as a base64 string; any other JSON type breaks the type assertion, so the helper fails fast instead of guessing.","triggerScenarios":"DecryptContext (or TestMasterKey_Encrypt) receives a secret whose Data[\"plaintext\"] is a non-string (number, bool, object) — typically from a mocked Vault response, a divergent Vault-compatible server, or middleware altering JSON types.","commonSituations":"Unit-test stubs returning plaintext as raw bytes/number instead of a base64 string; OpenBao or other Vault forks with changed response encoding; ingress proxies or custom plugins reshaping the decrypt response.","solutions":["Inspect the raw decrypt response (`vault write -format=json transit/decrypt/<key> ciphertext=...`) and confirm data.plaintext is a base64 string.","Fix any mock/stub to return {\"plaintext\":\"<base64>\"} with a string type.","Upgrade or align the Vault-compatible server so its transit decrypt response matches the standard schema.","Remove proxies/plugins that transform the JSON response between Vault and the client."],"exampleFix":"// before: malformed mock response\n{\"data\":{\"plaintext\":12345}}\n// after: transit-compatible response\n{\"data\":{\"plaintext\":\"c3VwZXJzZWNyZXQ=\"}}","handlingStrategy":"type-guard","validationCode":"raw, ok := secret.Data[\"plaintext\"]\nplaintext, isStr := raw.(string)\nif !ok || !isStr {\n    return fmt.Errorf(\"unexpected plaintext type %T; transit response schema mismatch\", raw)\n}\nif _, err := base64.StdEncoding.DecodeString(plaintext); err != nil {\n    return fmt.Errorf(\"plaintext is not valid base64\")\n}","typeGuard":"func asPlaintextString(secret *api.Secret) (string, bool) {\n    if secret == nil || secret.Data == nil { return \"\", false }\n    if v, ok := secret.Data[\"plaintext\"]; ok {\n        if s, isStr := v.(string); isStr { return s, true }\n    }\n    return \"\", false\n}","tryCatchPattern":"dataKey, err := dataKeyFromSecret(secret)\nif err != nil {\n    if strings.Contains(err.Error(), \"cannot be cast to string\") {\n        return fmt.Errorf(\"vault transit decrypt response schema mismatch (plaintext not a string): %w\", err)\n    }\n    return err\n}","preventionTips":["Keep mocks aligned with the real transit decrypt JSON schema","Test against real Vault (dev mode) in CI rather than only stubs","Log %T of the offending field when schema drift is suspected","Pin versions of Vault-compatible servers used in the pipeline"],"tags":["vault","transit","type-mismatch","go"],"backgroundTag":"vault-transit-response-type-mismatch","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}