{"record":{"id":"8e7e71212e8834cb","repo":"kubernetes/kops","slug":"error-parsing-assumerolepolicydocument-for-iamrole","errorCode":null,"errorMessage":"error parsing AssumeRolePolicyDocument for IAMRole %s: %v","messagePattern":"error parsing AssumeRolePolicyDocument for IAMRole (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/awstasks/iamrole.go","lineNumber":90,"sourceCode":"\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting role: %v\", err)\n\t}\n\n\tr := response.Role\n\tactual := &IAMRole{}\n\tactual.ID = r.RoleId\n\tactual.Name = r.RoleName\n\tif r.PermissionsBoundary != nil {\n\t\tactual.PermissionsBoundary = r.PermissionsBoundary.PermissionsBoundaryArn\n\t}\n\tif r.AssumeRolePolicyDocument != nil {\n\t\t// The AssumeRolePolicyDocument is URI encoded (?)\n\t\tactualPolicy := *r.AssumeRolePolicyDocument\n\t\tactualPolicy, err = url.QueryUnescape(actualPolicy)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing AssumeRolePolicyDocument for IAMRole %s: %v\", *e.Name, err)\n\t\t}\n\n\t\t// The RolePolicyDocument is reformatted by AWS\n\t\t// We parse both as JSON; if the json forms are equal we pretend the actual value is the expected value\n\t\tif e.RolePolicyDocument != nil {\n\t\t\texpectedPolicy, err := fi.ResourceAsString(e.RolePolicyDocument)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error reading expected RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)\n\t\t\t}\n\t\t\texpectedJson := make(map[string]interface{})\n\t\t\terr = json.Unmarshal([]byte(expectedPolicy), &expectedJson)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error parsing expected RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)\n\t\t\t}\n\t\t\tactualJson := make(map[string]interface{})\n\t\t\terr = json.Unmarshal([]byte(actualPolicy), &actualJson)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error parsing actual RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/awstasks/iamrole.go#L72-L108","documentation":"AWS returns the role's trust (assume-role) policy URL-encoded. Find() unescapes it before JSON comparison; if QueryUnescape fails, the error is wrapped with the role name. This is nearly always caused by malformed percent-encoding in the stored policy document.","triggerScenarios":"url.QueryUnescape(actualPolicy) returns an error, i.e. the AssumeRolePolicyDocument contains invalid escape sequences like %zz that AWS (or an intermediary) stored unexpectedly.","commonSituations":"Manual mutation of the trust policy via CLI/API producing non-standard encoding; proxy or state-store corruption; unusual characters in role session names inside the policy.","solutions":["Inspect the raw AssumeRolePolicyDocument with aws iam get-role --query 'Role.AssumeRolePolicyDocument' and correct invalid percent-encoding","Re-apply the expected trust policy from the kOps spec (kops update cluster) to overwrite the malformed document","If a proxy/middle layer is rewriting the document, bypass or fix it","Re-run reconciliation after the policy is normalized"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// validate unescaping works before processing\nactualPolicy := *r.AssumeRolePolicyDocument\ndecoded, err := url.QueryUnescape(actualPolicy)\nif err != nil { return fmt.Errorf(\"malformed percent-encoding in AssumeRolePolicyDocument: %w\", err) }","typeGuard":"func isValidEncodedPolicy(s string) bool {\n    _, err := url.QueryUnescape(s)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Never hand-edit trust policies with raw percent sequences","Re-apply policies via kOps so AWS normalizes encoding","Validate stored documents decode cleanly after any out-of-band change","Pin tooling versions that write policy documents"],"tags":["aws","iam","url-encoding","policy"],"backgroundTag":"url-decoding-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}