@@ -429,6 +429,144 @@ vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello")
429429res , err := rdb.Do (ctx, " set" , " key" , " value" ).Result ()
430430` ` `
431431
432+ ## Typed Errors
433+
434+ go-redis provides typed error checking functions for common Redis errors:
435+
436+ ` ` ` go
437+ // Cluster and replication errors
438+ redis.IsLoadingError (err) // Redis is loading the dataset
439+ redis.IsReadOnlyError (err) // Write to read-only replica
440+ redis.IsClusterDownError (err) // Cluster is down
441+ redis.IsTryAgainError (err) // Command should be retried
442+ redis.IsMasterDownError (err) // Master is down
443+ redis.IsMovedError (err) // Returns (address, true) if key moved
444+ redis.IsAskError (err) // Returns (address, true) if key being migrated
445+
446+ // Connection and resource errors
447+ redis.IsMaxClientsError (err) // Maximum clients reached
448+ redis.IsAuthError (err) // Authentication failed (NOAUTH, WRONGPASS, unauthenticated)
449+ redis.IsPermissionError (err) // Permission denied (NOPERM)
450+ redis.IsOOMError (err) // Out of memory (OOM)
451+
452+ // Transaction errors
453+ redis.IsExecAbortError (err) // Transaction aborted (EXECABORT)
454+ ` ` `
455+
456+ ### Error Wrapping in Hooks
457+
458+ When wrapping errors in hooks, use custom error types with ` Unwrap ()` method (preferred) or ` fmt.Errorf ` with ` %w` . Always call ` cmd.SetErr ()` to preserve error type information:
459+
460+ ` ` ` go
461+ // Custom error type (preferred)
462+ type AppError struct {
463+ Code string
464+ RequestID string
465+ Err error
466+ }
467+
468+ func (e *AppError) Error () string {
469+ return fmt.Sprintf (" [%s ] request_id=%s : %v " , e.Code , e.RequestID , e.Err )
470+ }
471+
472+ func (e *AppError) Unwrap () error {
473+ return e.Err
474+ }
475+
476+ // Hook implementation
477+ func (h MyHook) ProcessHook (next redis.ProcessHook ) redis.ProcessHook {
478+ return func (ctx context.Context , cmd redis.Cmder ) error {
479+ err := next (ctx, cmd)
480+ if err != nil {
481+ // Wrap with custom error type
482+ wrappedErr := &AppError{
483+ Code: " REDIS_ERROR" ,
484+ RequestID: getRequestID (ctx),
485+ Err: err,
486+ }
487+ cmd.SetErr (wrappedErr)
488+ return wrappedErr // Return wrapped error to preserve it
489+ }
490+ return nil
491+ }
492+ }
493+
494+ // Typed error detection works through wrappers
495+ if redis.IsLoadingError (err) {
496+ // Retry logic
497+ }
498+
499+ // Extract custom error if needed
500+ var appErr *AppError
501+ if errors.As (err, &appErr) {
502+ log.Printf (" Request: %s " , appErr.RequestID )
503+ }
504+ ` ` `
505+
506+ Alternatively, use ` fmt.Errorf ` with ` %w` :
507+ ` ` ` go
508+ wrappedErr := fmt.Errorf (" context: % w" , err)
509+ cmd.SetErr (wrappedErr)
510+ ` ` `
511+
512+ ### Pipeline Hook Example
513+
514+ For pipeline operations, use ` ProcessPipelineHook` :
515+
516+ ` ` ` go
517+ type PipelineLoggingHook struct {}
518+
519+ func (h PipelineLoggingHook) DialHook (next redis.DialHook ) redis.DialHook {
520+ return next
521+ }
522+
523+ func (h PipelineLoggingHook) ProcessHook (next redis.ProcessHook ) redis.ProcessHook {
524+ return next
525+ }
526+
527+ func (h PipelineLoggingHook) ProcessPipelineHook (next redis.ProcessPipelineHook ) redis.ProcessPipelineHook {
528+ return func (ctx context.Context , cmds []redis.Cmder ) error {
529+ start := time.Now ()
530+
531+ // Execute the pipeline
532+ err := next (ctx, cmds)
533+
534+ duration := time.Since (start)
535+ log.Printf (" Pipeline executed %d commands in %v " , len (cmds), duration)
536+
537+ // Process individual command errors
538+ // Note: Individual command errors are already set on each cmd by the pipeline execution
539+ for _ , cmd := range cmds {
540+ if cmdErr := cmd.Err (); cmdErr != nil {
541+ // Check for specific error types using typed error functions
542+ if redis.IsAuthError (cmdErr) {
543+ log.Printf (" Auth error in pipeline command %s : %v " , cmd.Name (), cmdErr)
544+ } else if redis.IsPermissionError (cmdErr) {
545+ log.Printf (" Permission error in pipeline command %s : %v " , cmd.Name (), cmdErr)
546+ }
547+
548+ // Optionally wrap individual command errors to add context
549+ // The wrapped error preserves type information through errors.As()
550+ wrappedErr := fmt.Errorf (" pipeline cmd %s failed: % w" , cmd.Name (), cmdErr)
551+ cmd.SetErr (wrappedErr)
552+ }
553+ }
554+
555+ // Return the pipeline-level error (connection errors, etc.)
556+ // You can wrap it if needed, or return it as-is
557+ return err
558+ }
559+ }
560+
561+ // Register the hook
562+ rdb.AddHook (PipelineLoggingHook{})
563+
564+ // Use pipeline - errors are still properly typed
565+ pipe := rdb.Pipeline ()
566+ pipe.Set (ctx, " key1" , " value1" , 0 )
567+ pipe.Get (ctx, " key2" )
568+ _ , err := pipe.Exec (ctx)
569+ ` ` `
432570
433571## Run the test
434572
0 commit comments