{"record":{"id":"9f147b6823f10b61","repo":"kubernetes/kops","slug":"error-parsing-policydocument-for-iamrolepolicy-q","errorCode":null,"errorMessage":"error parsing PolicyDocument for IAMRolePolicy %q: %v","messagePattern":"error parsing PolicyDocument for IAMRolePolicy %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/awstasks/iamrolepolicy.go","lineNumber":118,"sourceCode":"\tresponse, err := cloud.IAM().GetRolePolicy(ctx, request)\n\tif err != nil {\n\t\tif awsup.IsIAMNoSuchEntityException(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"error getting role: %v\", err)\n\t}\n\n\tp := response\n\tactual.Role = &IAMRole{Name: p.RoleName}\n\tif aws.ToString(e.Role.Name) == aws.ToString(p.RoleName) {\n\t\tactual.Role.ID = e.Role.ID\n\t}\n\tif p.PolicyDocument != nil {\n\t\t// The PolicyDocument is URI encoded (?)\n\t\tpolicy := *p.PolicyDocument\n\t\tpolicy, err = url.QueryUnescape(policy)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing PolicyDocument for IAMRolePolicy %q: %v\", aws.ToString(e.Name), err)\n\t\t}\n\n\t\t// Reformat the PolicyDocument by unmarshaling and re-marshaling to JSON.\n\t\t// This will make it possible to compare it when using CloudFormation.\n\t\tvar jsonData interface{}\n\t\terr = json.Unmarshal([]byte(policy), &jsonData)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing cloudformation policy document from JSON: %v\", err)\n\t\t}\n\t\tjsonBytes, err := json.MarshalIndent(jsonData, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error converting cloudformation policy document to JSON: %v\", err)\n\t\t}\n\t\tactual.PolicyDocument = fi.NewStringResource(string(jsonBytes))\n\t}\n\n\tactual.Name = p.PolicyName\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/awstasks/iamrolepolicy.go#L100-L136","documentation":"IAM returns the inline policy document URL-encoded; Find calls url.QueryUnescape to decode it. If decoding fails (malformed percent-encoding such as a stray '%' sequence), kops wraps the error with the policy name. This prevents comparing or diffing the policy document.","triggerScenarios":"GetRolePolicy returns a PolicyDocument string containing invalid percent-encodings (e.g. '%' not followed by two hex digits) that QueryUnescape cannot decode.","commonSituations":"A policy was created/modified outside kops (console, CloudFormation, other tooling) with an oddly encoded document; a bug or proxy mangling the API response; corrupted manual edits to the role policy.","solutions":["Inspect the raw PolicyDocument via `aws iam get-role-policy --role-name X --policy-name Y` for invalid percent-encoding","Re-apply the policy through kops so it is overwritten with a properly encoded document","Delete and recreate the offending inline policy with valid JSON encoding","Ensure no intermediary (proxy/SDK downgrade) is corrupting the IAM API response"],"exampleFix":"// before: manually created policy with bad encoding, kops fails to unescape\n// fix: overwrite it with a well-formed document\naws iam put-role-policy --role-name nodes --policy-name kops-policy --policy-document file://policy.json","handlingStrategy":"validation","validationCode":"// verify the stored policy decodes cleanly\nout, _ := iamClient.GetRolePolicy(ctx, req)\ndecoded, err := url.QueryUnescape(*out.PolicyDocument)\nif err != nil { // recreate the policy\n  iamClient.PutRolePolicy(ctx, &iam.PutRolePolicyInput{RoleName: &roleName, PolicyName: &policyName, PolicyDocument: aws.String(validJSON)})\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n  if r := recover(); r != nil || strings.Contains(lastErr, \"error parsing PolicyDocument\") {\n    // re-apply the policy via kops to overwrite the malformed document\n  }\n}()","preventionTips":["Avoid hand-editing IAM policies in the AWS console","Let kops own its inline policies exclusively","Validate externally created policies are URL-safe encoded JSON","Diff `aws iam get-role-policy` output against the kops spec"],"tags":["iam","url-decoding","policy-document"],"backgroundTag":"policy-document-parse-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"}