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

  1. Inspect the raw PolicyDocument via `aws iam get-role-policy --role-name X --policy-name Y` for invalid percent-encoding
  2. Re-apply the policy through kops so it is overwritten with a properly encoded document
  3. Delete and recreate the offending inline policy with valid JSON encoding
  4. 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

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/9f147b6823f10b61. Report an issue: GitHub.