grpc-ecosystem/grpc-gateway · error

failed to encode OpenAPI for %s: %w

Error message

failed to encode OpenAPI for %s: %w

What it means

In genopenapi's Generator.Generate, when the OpenAPI registry is in merged-output mode, all per-file OpenAPI docs are merged and encoded into a single file named by reg.GetMergeFileName(). If encodeOpenAPI fails (JSON/YAML marshaling of the merged swagger document), the error is wrapped as 'failed to encode OpenAPI for %s: %w'. The inner error identifies why the document could not be serialized.

Source

Thrown at protoc-gen-openapiv2/internal/genopenapi/generator.go:451

			continue
		}
		if err != nil {
			return nil, err
		}
		openapis = append(openapis, &wrapper{
			fileName: file.GetName(),
			swagger:  swagger,
		})
	}

	if g.reg.IsAllowMerge() {
		targetOpenAPI := mergeTargetFile(openapis, g.reg.GetMergeFileName())
		if !g.reg.IsPreserveRPCOrder() {
			targetOpenAPI.swagger.sortPathsAlphabetically()
		}
		f, err := encodeOpenAPI(targetOpenAPI, g.format)
		if err != nil {
			return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", g.reg.GetMergeFileName(), err)
		}
		files = append(files, f)
		if grpclog.V(1) {
			grpclog.Infof("New OpenAPI file will emit")
		}
	} else {
		for _, file := range openapis {
			if !g.reg.IsPreserveRPCOrder() {
				file.swagger.sortPathsAlphabetically()
			}
			f, err := encodeOpenAPI(file, g.format)
			if err != nil {
				return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", file.fileName, err)
			}
			files = append(files, f)
			if grpclog.V(1) {
				grpclog.Infof("New OpenAPI file will emit")
			}

View on GitHub (pinned to a58a4436a3)

Solutions

  1. Read the wrapped inner error to find the exact field/value that failed to marshal.
  2. Audit openapiv2 options in your protos for invalid extension values or unsupported types in custom fields.
  3. Try switching g.format (json vs yaml) to isolate whether the YAML or JSON encoder is at fault.
  4. Bisect by generating per-file output (disable merge) to find which input proto produces the bad content.

Example fix

// before: openapiv2_option with bad custom extension causing marshal failure
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { extensions: { key: "x-bad" value { struct_value { } } } };
// after: remove or fix the extension so it serializes
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "API" version: "1.0" } };
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate generated OpenAPI options serialize cleanly before generation:
_, err := json.Marshal(mergedSwaggerDoc)
if err != nil { log.Fatalf("openapi document not serializable: %v", err) }

Try / catch

if err := runProtocGenOpenAPIv2(); err != nil {
    var encErr *EncodingError
    if errors.As(err, &encErr) {
        log.Fatalf("failed to encode OpenAPI for %s: %v", encErr.File, errors.Unwrap(err))
    }
    return err
}

Prevention

When it happens

Trigger: Generate() with merge mode enabled (single output file configured) where encodeOpenAPI(targetOpenAPI, g.format) returns an error — typically json.MarshalIndent failures from invalid/unsupported values (e.g. NaN, malformed extension values) or YAML encoding problems in the merged document.

Common situations: Custom OpenAPI options or extensions inject values that don't serialize (invalid custom openapiv2 options); a broken plugin hook adds malformed fields; extremely deep nesting hitting marshal recursion issues.

Related errors


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