{"record":{"id":"ee0d3a94e931011a","repo":"amir20/dozzle","slug":"canceled-v-with-w","errorCode":null,"errorMessage":"canceled: %v with %w","messagePattern":"canceled: (.+?) with %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"internal/agent/client.go","lineNumber":123,"sourceCode":"}\n\nfunc rpcErrToErr(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tstatus, ok := status.FromError(err)\n\tif !ok {\n\t\treturn err\n\t}\n\n\tif status.Code() == codes.Unknown && status.Message() == \"EOF\" {\n\t\treturn fmt.Errorf(\"EOF error while converting gRPC to error: %w\", io.EOF)\n\t}\n\n\tswitch status.Code() {\n\tcase codes.Canceled:\n\t\treturn fmt.Errorf(\"canceled: %v with %w\", status.Message(), context.Canceled)\n\tcase codes.DeadlineExceeded:\n\t\treturn fmt.Errorf(\"deadline exceeded: %v with %w\", status.Message(), context.DeadlineExceeded)\n\tcase codes.Unknown:\n\t\treturn fmt.Errorf(\"unknown error: %v with %w\", status.Message(), err)\n\tcase codes.OK:\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown code: %v with %w\", status.Code(), err)\n\t}\n}\n\nfunc (c *Client) LogsBetweenDates(ctx context.Context, containerID string, since time.Time, until time.Time, std container.StdType) (<-chan *container.LogEvent, error) {\n\tstream, err := c.client.LogsBetweenDates(ctx, &pb.LogsBetweenDatesRequest{\n\t\tContainerId: containerID,\n\t\tSince:       timestamppb.New(since),\n\t\tUntil:       timestamppb.New(until),\n\t\tStreamTypes: int32(std),\n\t})","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/agent/client.go#L105-L141","documentation":"rpcErrToErr maps gRPC status code Canceled to \"canceled: %v with %w\", wrapping context.Canceled so callers can use errors.Is(err, context.Canceled). It indicates the RPC was cancelled, typically by the caller's own context or a client disconnect, not a server failure.","triggerScenarios":"Streaming calls (StreamEvents, StreamStats, sendLogs, etc.) whose ctx is cancelled: page navigation closing the SSE stream, client-side timeout, or explicit context cancel/shutdown.","commonSituations":"User closes a browser tab, cancelling the SSE request context; server shutdown cancelling all in-flight streams; a wrapped HTTP handler timing out and cancelling the request context.","solutions":["Check errors.Is(err, context.Canceled) and log at debug level; it is usually expected during shutdown/navigation","If cancellations are unexpected, audit who cancels the parent ctx (http request lifecycle, timeout wrappers)","Do not reconnect on Canceled; only resubscribe on genuine stream failures","Increase per-request deadlines if intentional timeouts are cutting streams short"],"exampleFix":"// before\nif err := <-streamErr; err != nil { log.Error().Err(err).Msg(\"stream error\") }\n// after\nif err := <-streamErr; err != nil {\n    if errors.Is(err, context.Canceled) { return } // expected on disconnect\n    log.Error().Err(err).Msg(\"stream error\")\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && errors.Is(err, context.Canceled) {\n    return // expected on client disconnect or shutdown; do not log as error\n}","preventionTips":["Never treat Canceled as a failure; it accompanies normal SSE teardown and shutdown","Tie stream contexts to request lifetimes so cancellation is deterministic","Avoid wrapping stream contexts in additional timeouts you do not control"],"tags":["go","grpc","context","streaming"],"backgroundTag":"context-canceled","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}