{"record":{"id":"e7d639db318e5ceb","repo":"grpc/grpc-go","slug":"no-error-details-for-status-with-code-ok","errorCode":null,"errorMessage":"no error details for status with code OK","messagePattern":"no error details for status with code OK","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/status/status.go","lineNumber":136,"sourceCode":"\tif s == nil {\n\t\treturn nil\n\t}\n\treturn proto.Clone(s.s).(*spb.Status)\n}\n\n// Err returns an immutable error representing s; returns nil if s.Code() is OK.\nfunc (s *Status) Err() error {\n\tif s.Code() == codes.OK {\n\t\treturn nil\n\t}\n\treturn &Error{s: s}\n}\n\n// WithDetails returns a new status with the provided details messages appended to the status.\n// If any errors are encountered, it returns nil and the first error encountered.\nfunc (s *Status) WithDetails(details ...protoadapt.MessageV1) (*Status, error) {\n\tif s.Code() == codes.OK {\n\t\treturn nil, errors.New(\"no error details for status with code OK\")\n\t}\n\t// s.Code() != OK implies that s.Proto() != nil.\n\tp := s.Proto()\n\tfor _, detail := range details {\n\t\tm, err := anypb.New(protoadapt.MessageV2Of(detail))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp.Details = append(p.Details, m)\n\t}\n\treturn &Status{s: p}, nil\n}\n\n// Details returns a slice of details messages attached to the status.\n// If a detail cannot be decoded, the error is returned in place of the detail.\n// If the detail can be decoded, the proto message returned is of the same\n// type that was given to WithDetails().\nfunc (s *Status) Details() []any {","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/status/status.go#L118-L154","documentation":"Returned by (*Status).WithDetails when the receiver Status has code codes.OK. gRPC forbids attaching rich error detail messages to a non-error status, since details are part of the error contract. WithDetails is only meaningful on statuses that represent an actual failure. The guard sits at internal/status/status.go:135-137 and returns a plain errors.New value.","triggerScenarios":"Calling status.New(codes.OK, \"\").WithDetails(detail), or invoking WithDetails on a Status whose code was never changed from the zero value (OK). Also happens when reusing a Status returned from a prior call that resolved to OK and then unconditionally chaining WithDetails.","commonSituations":"Programmatically building a status and forgetting to set a non-OK code; helper functions that always call WithDetails without checking whether an upstream call actually failed; copying error-detail logic from a failing path into a success path.","solutions":["Only call WithDetails when s.Code() != codes.OK; guard with an if before attaching details.","If you genuinely have no error, skip the WithDetails call entirely and return the OK status unchanged.","Use status.New with an explicit failing code (e.g. codes.Internal) before adding details.","Refactor helpers to take a *status.Status and early-return on OK instead of always appending details."],"exampleFix":"// before\nst := status.New(codes.OK, \"\")\nnewSt, err := st.WithDetails(detail) // returns error\n\n// after\nst := status.New(codes.Internal, \"boom\")\nif st.Code() != codes.OK {\n    newSt, err = st.WithDetails(detail)\n}","handlingStrategy":"validation","validationCode":"func safeWithDetails(st *status.Status, details ...protoadapt.MessageV1) (*status.Status, error) {\n    if st.Code() == codes.OK {\n        return st, nil // nothing to attach\n    }\n    return st.WithDetails(details...)\n}","typeGuard":"func isErrorStatus(st *status.Status) bool { return st != nil && st.Code() != codes.OK }","tryCatchPattern":null,"preventionTips":["Never call WithDetails unconditionally; branch on code != OK first.","Treat OK statuses as success and skip detail attachment.","Write helpers that accept a Status and decide internally whether to enrich it."],"tags":["go","grpc","status","validation"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}