grpc-ecosystem/grpc-gateway · error

%s: security: %w

Error message

%s: security: %w

What it means

This error is returned when the top-level `security` field of an input document cannot be json.Unmarshal-ed into the library's security slice type. The OpenAPI `security` field must be a JSON array of security requirement objects (e.g. [{"apiKey": []}]), so strings, objects, numbers, or null fail and are wrapped as `<name>: security: <cause>`.

Source

Thrown at openapiv3-merge/internal/merge/merge.go:232

			if err != nil {
				return nil, fmt.Errorf("%s: paths: %w", in.Name, err)
			}
			d.Paths = obj
		case "webhooks":
			obj, err := decodeOrderedObject(raw)
			if err != nil {
				return nil, fmt.Errorf("%s: webhooks: %w", in.Name, err)
			}
			d.Webhooks = obj
		case "components":
			if !isJSONNull(raw) {
				if err := json.Unmarshal(raw, d.Components); err != nil {
					return nil, fmt.Errorf("%s: components: %w", in.Name, err)
				}
			}
		case "security":
			if err := json.Unmarshal(raw, &d.Security); err != nil {
				return nil, fmt.Errorf("%s: security: %w", in.Name, err)
			}
		case "tags":
			if err := json.Unmarshal(raw, &d.Tags); err != nil {
				return nil, fmt.Errorf("%s: tags: %w", in.Name, err)
			}
		case "externalDocs":
			d.ExternalDocs = raw
		default:
			d.extras.set(key, raw)
		}
	}
	if _, err := dec.Token(); err != nil {
		return nil, fmt.Errorf("%s: %w", in.Name, err)
	}
	if d.OpenAPI == "" {
		return nil, fmt.Errorf("%s: missing required field \"openapi\"", in.Name)
	}
	if isJSONNull(d.Info) {

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Edit the input so `security` is an array of requirement objects, e.g. "security": [{"apiKey": []}].
  2. If a single requirement applies, wrap it: {"apiKey": []} becomes [{"apiKey": []}].
  3. If no global security applies, omit the key entirely (or use [] for an explicitly unsecured document).
  4. Pre-validate by unmarshalling the `security` value into []map[string][]string before calling Merge.

Example fix

// before
{"openapi": "3.1.0", "security": {"apiKey": []}}
// after
{"openapi": "3.1.0", "security": [{"apiKey": []}]}
Defensive patterns

Strategy: validation

Validate before calling

func validateSecurityField(data []byte) error {
	var doc struct {
		Security json.RawMessage `json:"security"`
	}
	if err := json.Unmarshal(data, &doc); err != nil {
		return err
	}
	if len(doc.Security) == 0 || string(doc.Security) == "null" {
		return nil
	}
	var s []map[string][]string
	if err := json.Unmarshal(doc.Security, &s); err != nil {
		return fmt.Errorf("security must be an array of requirement objects: %w", err)
	}
	return nil
}

Type guard

func isSecurityArray(raw json.RawMessage) bool {
	var s []map[string][]string
	return len(raw) > 0 && json.Unmarshal(raw, &s) == nil
}

Try / catch

merged, err := merger.Merge(inputs)
if err != nil {
	if strings.Contains(err.Error(), ": security: ") {
		return fmt.Errorf("input spec has a malformed security field: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling Merge with an Input whose Data contains `"security": "basicAuth"` (string), `"security": {"apiKey": []}` (bare object instead of array of objects), `"security": null`, or any non-array value under `security`.

Common situations: Hand-written specs where a single requirement object was written without the surrounding array; specs copied from OpenAPI 2.0 `securityDefinitions` usage; generated specs where Security was a map or string rather than a slice; templates with placeholder text under security.

Related errors


AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02). Data as JSON: /api/errors/974ab6caff9a12b4. Report an issue: GitHub.