kubernetes/kops · error
error parsing actual RolePolicyDocument for IAMRole %q: %v
Error message
error parsing actual RolePolicyDocument for IAMRole %q: %v
What it means
Find() also parses the actual (AWS-returned, unescaped) RolePolicyDocument as JSON. If the live trust policy on the role is not valid JSON, the error is wrapped with the role name. Since AWS normally stores valid JSON, this signals the document was mutated by something non-standard.
Source
Thrown at upup/pkg/fi/cloudup/awstasks/iamrole.go:108
return nil, fmt.Errorf("error parsing AssumeRolePolicyDocument for IAMRole %s: %v", *e.Name, err)
}
// The RolePolicyDocument is reformatted by AWS
// We parse both as JSON; if the json forms are equal we pretend the actual value is the expected value
if e.RolePolicyDocument != nil {
expectedPolicy, err := fi.ResourceAsString(e.RolePolicyDocument)
if err != nil {
return nil, fmt.Errorf("error reading expected RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
}
expectedJson := make(map[string]interface{})
err = json.Unmarshal([]byte(expectedPolicy), &expectedJson)
if err != nil {
return nil, fmt.Errorf("error parsing expected RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
}
actualJson := make(map[string]interface{})
err = json.Unmarshal([]byte(actualPolicy), &actualJson)
if err != nil {
return nil, fmt.Errorf("error parsing actual RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
}
if reflect.DeepEqual(actualJson, expectedJson) {
klog.V(2).Infof("actual RolePolicyDocument was json-equal to expected; returning expected value")
actualPolicy = expectedPolicy
}
}
actual.RolePolicyDocument = fi.NewStringResource(actualPolicy)
}
actual.Tags = mapIAMTagsToMap(r.Tags)
klog.V(2).Infof("found matching IAMRole %q", aws.ToString(actual.ID))
e.ID = actual.ID
// Avoid spurious changes
actual.ExportWithID = e.ExportWithID
actual.Lifecycle = e.LifecycleView on GitHub (pinned to 4c8573c808)
Solutions
- Inspect the live policy with aws iam get-role and fix or replace it with valid JSON
- Re-apply the desired policy from the kOps spec (kops update cluster --yes)
- Avoid manual edits to the trust policy; manage it solely through kOps
- If an external tool manages the role, reconcile its output format with kOps
Defensive patterns
Strategy: validation
Validate before calling
// validate the live policy decodes and parses before comparison
raw, _ := url.QueryUnescape(*r.AssumeRolePolicyDocument)
var v interface{}
if err := json.Unmarshal([]byte(raw), &v); err != nil {
// drift detected: live trust policy is not valid JSON
} Type guard
func livePolicyIsJSON(encoded string) bool {
raw, err := url.QueryUnescape(encoded)
if err != nil { return false }
var v interface{}
return json.Unmarshal([]byte(raw), &v) == nil
} Prevention
- Manage trust policies exclusively through kOps to prevent drift
- Audit roles for out-of-band edits (config drift detection)
- Re-apply the desired policy when live JSON becomes invalid
- Block console raw-edit workflows for kOps-managed roles
When it happens
Trigger: json.Unmarshal([]byte(actualPolicy), &actualJson) fails on the URL-unescaped AssumeRolePolicyDocument returned by GetRole.
Common situations: External tools (scripts, IaC drift) wrote a malformed trust policy directly to the role; double-encoding produced garbage after unescape; partial manual edits via console in raw mode.
Related errors
- error parsing expected RolePolicyDocument for IAMRole %q: %v
- error inline policy: %w
- additionalPolicy %q is invalid: %v
- error parsing AssumeRolePolicyDocument for IAMRole %s: %v
- error updating IAMRole: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/201469e1ab52ea36.
Report an issue: GitHub.