in router/core/graphql_handler.go [270:409]
func (h *GraphQLHandler) WriteError(ctx *resolve.Context, err error, res *resolve.GraphQLResponse, w io.Writer) {
reqContext := getRequestContext(ctx.Context())
if reqContext == nil {
h.log.Error("unable to get request context")
return
}
requestLogger := reqContext.logger
httpWriter, isHttpResponseWriter := w.(http.ResponseWriter)
response := GraphQLErrorResponse{
Errors: make([]graphqlError, 1),
Data: nil,
}
switch getErrorType(err) {
case errorTypeMergeResult:
var errMerge resolve.ErrMergeResult
if !errors.As(err, &errMerge) {
response.Errors[0].Message = "Internal server error"
return
}
response.Errors[0].Message = errMerge.Error()
case errorTypeRateLimit:
response.Errors[0].Message = "Rate limit exceeded"
if h.rateLimitConfig.ErrorExtensionCode.Enabled {
response.Errors[0].Extensions = &Extensions{
Code: h.rateLimitConfig.ErrorExtensionCode.Code,
}
}
if !h.rateLimitConfig.SimpleStrategy.HideStatsFromResponseExtension {
buf := bytes.NewBuffer(make([]byte, 0, 1024))
err = h.rateLimiter.RenderResponseExtension(ctx, buf)
if err != nil {
requestLogger.Error("unable to render rate limit stats", zap.Error(err))
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
return
}
response.Extensions = &Extensions{
RateLimit: buf.Bytes(),
}
}
if isHttpResponseWriter {
httpWriter.WriteHeader(h.rateLimiter.RejectStatusCode())
}
case errorTypeUnauthorized:
response.Errors[0].Message = "Unauthorized"
if h.authorizer.HasResponseExtensionData(ctx) {
buf := bytes.NewBuffer(make([]byte, 0, 1024))
err = h.authorizer.RenderResponseExtension(ctx, buf)
if err != nil {
requestLogger.Error("unable to render authorization extension", zap.Error(err))
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
return
}
response.Extensions = &Extensions{
Authorization: buf.Bytes(),
}
}
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusOK) // Always return 200 OK when we return a well-formed response
}
case errorTypeContextCanceled:
response.Errors[0].Message = "Client disconnected"
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusRequestTimeout)
}
case errorTypeContextTimeout:
response.Errors[0].Message = "Server timeout"
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusRequestTimeout)
}
case errorTypeUnknown:
response.Errors[0].Message = "Internal server error"
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
case errorTypeUpgradeFailed:
var upgradeErr *graphql_datasource.UpgradeRequestError
if h.subgraphErrorPropagation.PropagateStatusCodes && errors.As(err, &upgradeErr) && upgradeErr.StatusCode != 0 {
response.Errors[0].Extensions = &Extensions{
StatusCode: upgradeErr.StatusCode,
}
if subgraph := reqContext.subgraphResolver.BySubgraphURL(upgradeErr.URL); subgraph != nil {
response.Errors[0].Message = fmt.Sprintf("Subscription Upgrade request failed for Subgraph '%s'.", subgraph.Name)
} else {
response.Errors[0].Message = "Subscription Upgrade request failed"
}
} else {
response.Errors[0].Message = "Subscription Upgrade request failed"
}
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusOK)
}
case errorTypeEDFS:
response.Errors[0].Message = fmt.Sprintf("EDFS error: %s", err.Error())
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
case errorTypeInvalidWsSubprotocol:
response.Errors[0].Message = fmt.Sprintf("Invalid Subprotocol error: %s or configure the subprotocol to be used using `wgc subgraph update` command.", err.Error())
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
case errorTypeEDFSInvalidMessage:
response.Errors[0].Message = "Invalid message received"
if isHttpResponseWriter {
httpWriter.WriteHeader(http.StatusInternalServerError)
}
}
if ctx.TracingOptions.Enable && ctx.TracingOptions.IncludeTraceOutputInResponseExtensions {
traceNode := resolve.GetTrace(ctx.Context(), res.Fetches)
if response.Extensions == nil {
response.Extensions = &Extensions{}
}
response.Extensions.Trace, err = json.Marshal(traceNode)
if err != nil {
requestLogger.Error("Unable to marshal trace node", zap.Error(err))
}
}
err = json.NewEncoder(w).Encode(response)
if err != nil {
if rErrors.IsBrokenPipe(err) {
requestLogger.Warn("Broken pipe, unable to write error response", zap.Error(err))
} else {
requestLogger.Error("Unable to write error response", zap.Error(err))
}
}
if wsRw, ok := w.(*websocketResponseWriter); ok {
_ = wsRw.Flush()
}
}