grpc-ecosystem/grpc-gateway · error

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

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.

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)

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.