grpc/grpc-go · warning
failed to close exporter. Flush failed: %v; Close failed: %v
Error message
failed to close exporter. Flush failed: %v; Close failed: %v
What it means
cloudLoggingExporter.Close() ran and BOTH the logger Flush and the client Close returned an error. This is a best-effort cleanup path (stopLogging -> lExporter.Close); Flush pushes buffered log entries upstream and Close tears down the HTTP connection to Cloud Logging. Seeing both means telemetry was likely lost and the connection did not shut down cleanly.
Source
Thrown at gcp/observability/exporting.go:96
}
func (cle *cloudLoggingExporter) EmitGcpLoggingEntry(entry gcplogging.Entry) {
cle.logger.Log(entry)
if logger.V(2) {
logger.Infof("Uploading event to CloudLogging: %+v", entry)
}
}
func (cle *cloudLoggingExporter) Close() error {
var errFlush, errClose error
if cle.logger != nil {
errFlush = cle.logger.Flush()
}
if cle.client != nil {
errClose = cle.client.Close()
}
if errFlush != nil && errClose != nil {
return fmt.Errorf("failed to close exporter. Flush failed: %v; Close failed: %v", errFlush, errClose)
}
if errFlush != nil {
return errFlush
}
if errClose != nil {
return errClose
}
cle.logger = nil
cle.client = nil
logger.Infof("Closed CloudLogging exporter")
return nil
}
View on GitHub (pinned to 0c51461d27)
Solutions
- Log both inner errors and treat them as best-effort: shutdown telemetry loss is usually acceptable; document it.
- If Flush consistently fails, call Flush explicitly before End with a still-valid network path (don't wait for shutdown).
- Refresh Application Default Credentials so Close does not get a 401.
- For tests, ensure the exporter is closed exactly once and the mock simulates Flush success.
- Increase gcplogging DelayThreshold / BufferedByteLimit tradeoffs so less data is pending at shutdown.
Example fix
// before
defer observability.End()
// after
defer func() {
if err := observability.End(); err != nil {
log.Printf("observability shutdown (non-fatal): %v", err)
}
}() Defensive patterns
Strategy: try-catch
Try / catch
// End/Close is best-effort; never fail shutdown on telemetry flush.
defer func() {
if err := observability.End(); err != nil {
log.Printf("observability shutdown error (non-fatal): %v", err)
}
}() Prevention
- Treat exporter Close failures as warnings, not fatal.
- Call Flush earlier in the lifecycle so shutdown has less to drain.
- Refresh ADC before long-running processes to avoid expired-token errors at Close.
When it happens
Trigger: Calling observability.End() (or a failed Start's deferred End) while the network is down, ADC has expired, the project is gone, or the process is being torn down mid-flush. Either side alone returns its own error; only when both fail do you see this combined message.
Common situations: Process killed during shutdown so Flush races a closing transport; ADC token expired between Start and End; Cloud Logging API disabled/quota exhausted mid-run; long-lived process whose connection was idle-closed; unit test that force-closes the mock exporter twice.
Related errors
- invalid method string: %v, err: %v
- error in clientRPCEvent method: %v
- error in serverRPCEvent method: %v
- failed to create cloudLoggingExporter: %v
- unable to create CloudLogging exporter: %v
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/5961baa2822f37bb.
Report an issue: GitHub.