grpc-ecosystem/grpc-gateway · error

must not set request body when http method is DELETE except…

Error message

must not set request body when http method is DELETE except allow_delete_body option is true: %s

What it means

DELETE with a request body is discouraged and not supported by default; the generator only allows a 'body' on a 'delete' rule when the allow_delete_body option is explicitly enabled. Otherwise template building fails with the method descriptor name.

Solutions

  1. Remove the 'body' field from the DELETE annotation and pass identifiers via the path template
  2. If a body is genuinely required, enable the allow_delete_body generator option
  3. Replace the rpc with a POST-based bulk action (e.g. '/v1/items:batchDelete')
  4. Align the annotation with REST conventions: DELETE targets identified by path

Example fix

// before
option (google.api.http) = { delete: "/v1/items" body: "*" };
// after
option (google.api.http) = { post: "/v1/items:batchDelete" body: "*" };
Defensive patterns

Strategy: validation

Validate before calling

if rule.GetDelete() != "" && rule.GetBody() != "" && !allowDeleteBody {
    return fmt.Errorf("rpc %s: DELETE with body requires allow_delete_body", rpc.GetName())
}
err := buildTemplates(rpc)

Prevention

When it happens

Trigger: A rpc's google.api.http option sets both 'delete' and a non-empty 'body', while Registry.allowDeleteBody is false (the default, set via CLI flag).

Common situations: Copy-pasting a POST annotation and changing only the verb to delete; bulk-delete rpcs that want a body; teams unaware that the generator requires an opt-in flag for this nonstandard pattern.

Related errors


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

Appendix: source

Thrown at internal/descriptor/services.go:123

			httpMethod = "GET"
			pathTemplate = opts.GetGet()
			if opts.Body != "" {
				return nil, fmt.Errorf("must not set request body when http method is GET: %s", md.GetName())
			}

		case opts.GetPut() != "":
			httpMethod = "PUT"
			pathTemplate = opts.GetPut()

		case opts.GetPost() != "":
			httpMethod = "POST"
			pathTemplate = opts.GetPost()

		case opts.GetDelete() != "":
			httpMethod = "DELETE"
			pathTemplate = opts.GetDelete()
			if opts.Body != "" && !r.allowDeleteBody {
				return nil, fmt.Errorf("must not set request body when http method is DELETE except allow_delete_body option is true: %s", md.GetName())
			}

		case opts.GetPatch() != "":
			httpMethod = "PATCH"
			pathTemplate = opts.GetPatch()

		case opts.GetCustom() != nil:
			custom := opts.GetCustom()
			httpMethod = custom.Kind
			pathTemplate = custom.Path

		default:
			if grpclog.V(1) {
				grpclog.Infof("No pattern specified in google.api.HttpRule: %s", md.GetName())
			}
			return nil, nil
		}

View on GitHub (pinned to a58a4436a3)