Skip to content

Misleading 'unexpected end of JSON input' error when adoption-fields annotation is missing with adoption-policy: adopt #2824

Description

@shinichitazawa

Describe the bug

When using services.k8s.aws/adoption-policy: adopt annotation without the services.k8s.aws/adoption-fields annotation, the controller returns an unclear unexpected end of JSON input error. This error becomes a TerminalError, permanently blocking reconciliation.

The error message does not indicate that the adoption-fields annotation is missing, making it very difficult to diagnose.

Reproduction Steps

  1. Create a Role CR with adoption-policy: adopt but without adoption-fields:
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
  name: my-existing-role
  namespace: ack-system
  annotations:
    services.k8s.aws/region: ap-northeast-1
    services.k8s.aws/adoption-policy: adopt
    # NOTE: services.k8s.aws/adoption-fields is intentionally omitted
spec:
  name: my-existing-role
  assumeRolePolicyDocument: |
    { ... }
  1. Apply the resource:
kubectl apply -f role.yaml
  1. Controller logs show:
{"level":"error","msg":"Reconciler error","controller":"role","error":"unexpected end of JSON input"}

The resource enters a terminal error state with no way to recover except re-creating with the correct annotation.

Expected Behavior

The error message should clearly state that the services.k8s.aws/adoption-fields annotation is required when adoption-policy is set to adopt, for example:

services.k8s.aws/adoption-fields annotation is required when adoption-policy is set to 'adopt'; 
please provide the resource identifier fields as a JSON object (e.g. '{"name": "my-resource"}')

Root Cause

In pkg/runtime/util.go (ExtractAdoptionFields):

func ExtractAdoptionFields(res acktypes.AWSResource) (map[string]string, error) {
    fields := getAdoptionFields(res)  // returns "" when annotation is missing
    extractedFields := &map[string]string{}
    err := json.Unmarshal([]byte(fields), extractedFields)  // json.Unmarshal("") → "unexpected end of JSON input"
    if err != nil {
        return nil, err
    }
    return *extractedFields, nil
}

When adoption-fields annotation is not set, getAdoptionFields() returns an empty string "". Then json.Unmarshal([]byte(""), ...) produces the generic Go error unexpected end of JSON input.

In reconciler.go, this error is then wrapped as a TerminalError for adopt policy (but silently ignored for adopt-or-create).

Proposed Fix

Add an explicit empty string check before calling json.Unmarshal, and wrap the JSON parse error with context:

func ExtractAdoptionFields(res acktypes.AWSResource) (map[string]string, error) {
    fields := getAdoptionFields(res)
    if fields == "" {
        return nil, fmt.Errorf(
            "services.k8s.aws/adoption-fields annotation is required when " +
            "adoption-policy is set to 'adopt'; please provide the resource " +
            "identifier fields as a JSON object (e.g. '{\"name\": \"my-resource\"}')",
        )
    }
    extractedFields := &map[string]string{}
    err := json.Unmarshal([]byte(fields), extractedFields)
    if err != nil {
        return nil, fmt.Errorf("failed to parse services.k8s.aws/adoption-fields annotation as JSON: %w", err)
    }
    return *extractedFields, nil
}

I have a branch with the fix and tests ready to submit as a PR once this issue is triaged.

Environment

  • ACK IAM Controller: v1.6.2
  • ACK Runtime: v0.58.0
  • Kubernetes: EKS 1.31
  • Region: ap-northeast-1

Related Issues

/kind bug
/area runtime
/area adoption-annotation

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/adoption-annotationIssues or PRs related to ACK Adoption by Annotation featurearea/runtimeIssues or PRs as related to controller runtime, common reconciliation logic, etckind/bugCategorizes issue or PR as related to a bug.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions