cloudflare/cloudflared · error

aud field is not a string or an array of strings

Error message

aud field is not a string or an array of strings

What it means

jwtPayload.UnmarshalJSON requires the "aud" claim to be either a JSON string or an array of strings. Any other JSON type (number, object, boolean, null) hits the default branch and returns this error, rejecting the token as structurally invalid.

Source

Thrown at token/token.go:96

	var audParser struct {
		Aud any `json:"aud"`
	}
	if err := json.Unmarshal(data, &audParser); err != nil {
		return err
	}
	switch aud := audParser.Aud.(type) {
	case string:
		p.Aud = []string{aud}
	case []any:
		for _, a := range aud {
			s, ok := a.(string)
			if !ok {
				return errors.New("aud array contains non-string elements")
			}
			p.Aud = append(p.Aud, s)
		}
	default:
		return errors.New("aud field is not a string or an array of strings")
	}
	return nil
}

func (p jwtPayload) isExpired() bool {
	return int(time.Now().Unix()) > p.Exp
}

const (
	lockRetryInterval  = 2 * time.Second
	lockTimeout        = 10 * time.Minute
	startTimeTolerance = int64(1000) // milliseconds
)

// acquireLockFile loops until it successfully creates a lock file for the
// given token file path. The lock file is created at tokenPath + ".lock".
//
// On each iteration:

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Fix the token issuer to emit aud as a string or an array of strings, e.g. "aud": "app-id" or ["app-id"].
  2. Decode the token payload (base64 JSON) and confirm the aud claim's JSON type before debugging further.
  3. If the issuer is fixed but cached tokens persist, discard/regenerate the cached tokens.

Example fix

// before: {"aud": 12345}
// after: {"aud": ["12345"]}
Defensive patterns

Strategy: validation

Validate before calling

switch payloadMap["aud"].(type) {
case string, []any:
    // acceptable shape (array still needs string-element check)
default:
    return fmt.Errorf("aud must be a string or array of strings, got %T", payloadMap["aud"])
}

Type guard

func hasValidAudShape(v any) bool {
    switch t := v.(type) {
    case string:
        return t != ""
    case []any:
        return len(t) > 0
    default:
        return false
    }
}

Try / catch

p := jwtPayload{}
if err := json.Unmarshal(rawPayload, &p); err != nil {
    if strings.Contains(err.Error(), "aud field") {
        return fmt.Errorf("token rejected: aud claim must be string or []string: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Unmarshalling a JWT payload whose aud claim is not a string or string-array, e.g. "aud": 12345, "aud": {"tenant":"x"}, or "aud": true.

Common situations: Custom token minters that serialize audience as a numeric client ID or nested object, tokens issued by non-standard services, or manually edited/hand-rolled JWTs used in testing.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/19246257f2559296. Report an issue: GitHub.