ข้ามไปยังเนื้อหา

Errors & Status Codes

gRPC ไม่ใช้ HTTP status code ทุก call จบด้วย gRPC status: code ที่เป็นตัวเลข, message ที่มนุษย์อ่านได้ (optional), และ details แบบมีโครงสร้าง (optional) call ที่สำเร็จ return OK; อย่างอื่นคือ error ที่ client รับมาเป็น status ที่มี type ไม่ใช่ response body ที่ต้อง parse

การเลือก code ที่ถูกต้องคือการตัดสินใจออกแบบ — client แตกเงื่อนไขจาก code, retry ตาม code และถูกปลุกกลางดึกด้วย code code ที่คลุมเครือทำให้ API น่าหงุดหงิด; code ที่แม่นยำทำให้ API อธิบายตัวเองได้

มี code ประมาณ 17 ตัว แต่ไม่กี่ตัวครอบคลุมเกือบทุกอย่าง:

Codeความหมายสาเหตุที่พบบ่อย
OKสำเร็จ
INVALID_ARGUMENTrequest ผิดรูปแบบclient ส่ง field ผิด; แก้ input แล้วจะช่วยได้
NOT_FOUNDentity ไม่มีอยู่GetUser กับ id ที่ไม่มี
ALREADY_EXISTSentity มีอยู่แล้วCreateUser ด้วย email ที่ถูกใช้ไปแล้ว
PERMISSION_DENIEDauthenticate แล้วแต่ไม่ได้รับอนุญาตcaller ไม่มี role
UNAUTHENTICATEDไม่มี credential ที่ถูกต้องtoken หายหรือผิด
FAILED_PRECONDITIONstate ของระบบไม่พร้อมสำหรับ call นี้ลบ bucket ที่ยังไม่ว่าง
RESOURCE_EXHAUSTEDชน quota หรือ rate limitrequest มากเกินไป
DEADLINE_EXCEEDEDcall รันเกิน deadlineserver หรือ network ช้า
UNAVAILABLEserver ล่มหรือติดต่อไม่ได้ชั่วคราว; มักปลอดภัยที่จะ retry
INTERNALบั๊กฝั่ง serverfailure ที่ไม่คาดคิด

มี 2 คู่ที่คนสับสนบ่อย:

  • INVALID_ARGUMENT vs FAILED_PRECONDITION — ตัวแรกหมายถึง “input คุณผิด แก้แล้วจะช่วยได้”; ตัวหลังหมายถึง “input คุณโอเค แต่ระบบไม่ได้อยู่ใน state ที่ทำสำเร็จได้”
  • UNAUTHENTICATED vs PERMISSION_DENIED — ตัวแรกหมายถึง “ฉันไม่รู้ว่าคุณเป็นใคร”; ตัวหลังหมายถึง “ฉันรู้ว่าคุณเป็นใคร และคุณไม่ได้รับอนุญาต”

UNAVAILABLE และ DEADLINE_EXCEEDED สำคัญต่อการ retry: ทั้งคู่บอกว่าเป็น failure ชั่วคราว client (และ interceptor) จึงมัก retry ต่อ ส่วน INVALID_ARGUMENT ไม่ควร retry แบบเดิมซ้ำเด็ดขาด

คุณไม่ได้ return ข้อความ error ใน response — คุณจบ call ด้วย status ทุกภาษาเปิดเผยไอเดียเดียวกัน:

import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s *server) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.User, error) {
u, ok := s.store[req.Id]
if !ok {
return nil, status.Errorf(codes.NotFound, "user %d not found", req.Id)
}
return u, nil
}

ฝั่ง client จะได้ status เดียวกันกลับมาเป็น error ที่มี type ซึ่งพก code และ message มาด้วย — caller จึงแตกเงื่อนไข NOT_FOUND กับ UNAVAILABLE ได้โดยไม่ต้อง match string

code กับ message มักจะพอ เมื่อไม่พอ — เมื่อ client ต้องการรายละเอียดที่ machine-readable เช่น field ไหน validate ไม่ผ่าน — gRPC มี model ที่รวยกว่า: google.rpc.Status พก list details ของ message ที่มี type ได้หลายตัว

flowchart LR
  err["gRPC status"] --> code["code: INVALID_ARGUMENT"]
  err --> msg["message: validation failed"]
  err --> det["details[]"]
  det --> bad["BadRequest {
 field: email,
 desc: not a valid address }"]
error ของ gRPC: code + message และ details แบบมีโครงสร้าง (optional)

detail type มาตรฐาน (BadRequest, ErrorInfo, QuotaFailure, RetryInfo และอื่น ๆ จาก google.rpc) ทำให้ server บอกได้แม่น ๆ ว่า “field email ไม่ถูกต้อง” หรือ “retry หลังจาก 3 วินาที” และทำให้ client react แบบ programmatic ได้ ใช้ code เปล่า ๆ สำหรับเคสทั่วไป; หยิบ rich details มาใช้เมื่อ client ต้องการ error data แบบมีโครงสร้างจริง ๆ

gRPC call รายงาน failure อย่างไร?
client ส่ง request ที่รูปแบบถูกต้อง แต่ bucket เป้าหมายยังไม่ว่าง จึงลบไม่ได้ code ไหนเหมาะที่สุด?
ความต่างระหว่าง UNAUTHENTICATED กับ PERMISSION_DENIED คืออะไร?
เมื่อไรควรใช้ google.rpc.Status details แทนที่จะใช้แค่ code กับ message?