grpc-ecosystem/grpc-gateway · error

failed to parse OpenAPI Configuration from YAML in

Error message

failed to parse OpenAPI Configuration from YAML in %q: %w

What it means

loadOpenAPIConfigFromYAML fails when the YAML is syntactically valid but does not conform to the OpenAPIConfig proto message: protojson.Unmarshal with DiscardUnknown:false rejects unknown fields, wrong types, or misspelled keys. The YAML is first converted to JSON, then decoded into openapiconfig.OpenAPIConfig.

Solutions

  1. Remove or rename unknown fields to match openapiv2.OpenAPIConfig field names (snake_case of the proto fields)
  2. Check the OpenAPIConfig proto definition for your version and update config accordingly
  3. Temporarily diff the config against a working example from the same library version
  4. Pin the generator and config to compatible versions when upgrading

Example fix

// before
openapiOptions:
  openapi_operation_optionz:
    - selector: "my.service.Foo"
// after
openapiOptions:
  openapiOperationOptions:
    - selector: "my.service.Foo"
Defensive patterns

Strategy: validation

Validate before calling

var raw map[string]interface{}
if err := yaml.Unmarshal(contents, &raw); err != nil { return err }
allowed := map[string]bool{"type": true, "openapiOptions": true, /* fields of OpenAPIConfig */}
for k := range raw {
    if !allowed[k] {
        return fmt.Errorf("unknown config key %q", k)
    }
}

Try / catch

if err := reg.LoadOpenAPIConfigFromYAML(path); err != nil {
    var uerr *protojson.UnmarshalError
    if errors.As(err, &uerr) || strings.Contains(err.Error(), "unknown field") {
        log.Fatalf("config field mismatch in %s: %v", path, err)
    }
    return err
}

Prevention

When it happens

Trigger: LoadOpenAPIConfigFromYAML given a config containing keys not present in OpenAPIConfig (e.g. typos like 'openapi_options' instead of 'openapiOptions'-equivalent snake_case names, or unexpected nested fields).

Common situations: Upgrading protoc-gen-openapiv2 to a version that removed/renamed a config field; following outdated documentation; typo in a field name; wrong casing of snake_case field names.

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 grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02). Data as JSON: /api/errors/6f17b8aa51445893. Report an issue: GitHub.

Appendix: source

Thrown at internal/descriptor/openapi_configuration.go:31

func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*openapiconfig.OpenAPIConfig, error) {
	var yamlContents interface{}
	if err := yaml.Unmarshal(yamlFileContents, &yamlContents); err != nil {
		return nil, fmt.Errorf("failed to parse gRPC API Configuration from YAML in %q: %w", yamlSourceLogName, err)
	}

	jsonContents, err := json.Marshal(yamlContents)
	if err != nil {
		return nil, err
	}

	// Reject unknown fields because OpenAPIConfig is only used here
	unmarshaler := protojson.UnmarshalOptions{
		DiscardUnknown: false,
	}

	openapiConfiguration := openapiconfig.OpenAPIConfig{}
	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
		return nil, fmt.Errorf("failed to parse OpenAPI Configuration from YAML in %q: %w", yamlSourceLogName, err)
	}

	return &openapiConfiguration, nil
}

func registerOpenAPIOptions(registry *Registry, openAPIConfig *openapiconfig.OpenAPIConfig, yamlSourceLogName string) error {
	if openAPIConfig.OpenapiOptions == nil {
		// Nothing to do
		return nil
	}

	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
		return fmt.Errorf("failed to register option in %s: %w", yamlSourceLogName, err)
	}
	return nil
}

// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file

View on GitHub (pinned to a58a4436a3)