grpc/grpc-go · warning

no error details for status with code OK

Error message

no error details for status with code OK

What it means

Returned by Status.WithDetails when the Status has code OK. gRPC's status invariant dictates that an OK status represents success and must never carry error details (details are rich metadata for failures). WithDetails explicitly rejects adding details to an OK status to prevent creating a semantically contradictory state. Details are only meaningful attached to non-OK codes.

Solutions

  1. Only call WithDetails when the status code is not OK: check st.Code() != codes.OK first.
  2. Construct the status with a non-OK code before adding details: status.New(codes.InvalidArgument, "bad input").
  3. Refactor shared code to guard the WithDetails call with a code check.
  4. If you need metadata on a successful response, use response headers/trailers, not status details.

Example fix

// before
st := status.New(codes.OK, "success")
st, err := st.WithDetails(detail) // error!
// after
st := status.New(codes.InvalidArgument, "invalid input")
st, err := st.WithDetails(detail) // ok

// or guard:
if st.Code() != codes.OK {
    st, _ = st.WithDetails(detail)
}
Defensive patterns

Strategy: validation

Validate before calling

// Only call WithDetails on non-OK statuses.
func safeWithDetails(st *status.Status, details ...protoadapt.MessageV1) (*status.Status, error) {
    if st.Code() == codes.OK {
        return st, nil // no details for success
    }
    return st.WithDetails(details...)
}

Try / catch

st, err := st.WithDetails(detail)
if err != nil && strings.Contains(err.Error(), "code OK") {
    // programming error: don't add details to OK status
    log.Print("warning: attempted to add details to OK status")
}

Prevention

When it happens

Trigger: Calling st.WithDetails(detailProto) on a Status created with status.New(codes.OK, "") or obtained from a successful RPC. This is a programming error—the caller should only attach details to failure statuses.

Common situations: Interceptor or handler code that attaches details unconditionally without checking the code; error-wrapping utilities that call WithDetails on whatever status they receive, including success; copy-paste from error-handling code into a success path.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/e7d639db318e5ceb. Report an issue: GitHub.

Appendix: source

Thrown at internal/status/status.go:136

	if s == nil {
		return nil
	}
	return proto.Clone(s.s).(*spb.Status)
}

// Err returns an immutable error representing s; returns nil if s.Code() is OK.
func (s *Status) Err() error {
	if s.Code() == codes.OK {
		return nil
	}
	return &Error{s: s}
}

// WithDetails returns a new status with the provided details messages appended to the status.
// If any errors are encountered, it returns nil and the first error encountered.
func (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {
	if s.Code() == codes.OK {
		return nil, errors.New("no error details for status with code OK")
	}
	// s.Code() != OK implies that s.Proto() != nil.
	p := s.Proto()
	for _, detail := range details {
		m, err := anypb.New(protoadapt.MessageV2Of(detail))
		if err != nil {
			return nil, err
		}
		p.Details = append(p.Details, m)
	}
	return &Status{s: p}, nil
}

// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
// If the detail can be decoded, the proto message returned is of the same
// type that was given to WithDetails().
func (s *Status) Details() []any {

View on GitHub (pinned to 0c51461d27)