kubernetes/kops · error
error parsing PolicyDocument for IAMRolePolicy %q: %v
Error message
error parsing PolicyDocument for IAMRolePolicy %q: %v
What it means
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.
Source
Thrown at upup/pkg/fi/cloudup/awstasks/iamrolepolicy.go:118
response, err := cloud.IAM().GetRolePolicy(ctx, request)
if err != nil {
if awsup.IsIAMNoSuchEntityException(err) {
return nil, nil
}
return nil, fmt.Errorf("error getting role: %v", err)
}
p := response
actual.Role = &IAMRole{Name: p.RoleName}
if aws.ToString(e.Role.Name) == aws.ToString(p.RoleName) {
actual.Role.ID = e.Role.ID
}
if p.PolicyDocument != nil {
// The PolicyDocument is URI encoded (?)
policy := *p.PolicyDocument
policy, err = url.QueryUnescape(policy)
if err != nil {
return nil, fmt.Errorf("error parsing PolicyDocument for IAMRolePolicy %q: %v", aws.ToString(e.Name), err)
}
// Reformat the PolicyDocument by unmarshaling and re-marshaling to JSON.
// This will make it possible to compare it when using CloudFormation.
var jsonData interface{}
err = json.Unmarshal([]byte(policy), &jsonData)
if err != nil {
return nil, fmt.Errorf("error parsing cloudformation policy document from JSON: %v", err)
}
jsonBytes, err := json.MarshalIndent(jsonData, "", " ")
if err != nil {
return nil, fmt.Errorf("error converting cloudformation policy document to JSON: %v", err)
}
actual.PolicyDocument = fi.NewStringResource(string(jsonBytes))
}
actual.Name = p.PolicyName
View on GitHub (pinned to 4c8573c808)
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
Example fix
// before: manually created policy with bad encoding, kops fails to unescape // fix: overwrite it with a well-formed document aws iam put-role-policy --role-name nodes --policy-name kops-policy --policy-document file://policy.json
Defensive patterns
Strategy: validation
Validate before calling
// verify the stored policy decodes cleanly
out, _ := iamClient.GetRolePolicy(ctx, req)
decoded, err := url.QueryUnescape(*out.PolicyDocument)
if err != nil { // recreate the policy
iamClient.PutRolePolicy(ctx, &iam.PutRolePolicyInput{RoleName: &roleName, PolicyName: &policyName, PolicyDocument: aws.String(validJSON)})
} Try / catch
defer func() {
if r := recover(); r != nil || strings.Contains(lastErr, "error parsing PolicyDocument") {
// re-apply the policy via kops to overwrite the malformed document
}
}() Prevention
- 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
When it happens
Trigger: GetRolePolicy returns a PolicyDocument string containing invalid percent-encodings (e.g. '%' not followed by two hex digits) that QueryUnescape cannot decode.
Common situations: 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.
Related errors
- error parsing cloudformation policy document from JSON: %v
- error converting cloudformation policy document to JSON: %v
- error rendering PolicyDocument: %v
- error reading actual policy document: %v
- IP version is incorrect
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/9f147b6823f10b61.
Report an issue: GitHub.