From 4e68cf5b1e61e644a228ab83c043b4c27581cfeb Mon Sep 17 00:00:00 2001 From: Ruslan Semagin Date: Thu, 19 Feb 2026 07:51:46 +0300 Subject: [PATCH] refactor: replace direct error comparisons with errors.Is Improves correctness when working with wrapped errors --- pkg/channels/line.go | 3 ++- pkg/providers/codex_cli_provider.go | 3 ++- pkg/providers/codex_provider.go | 4 ++-- pkg/providers/error_classifier.go | 5 +++-- pkg/providers/error_classifier_test.go | 2 +- pkg/providers/fallback.go | 9 +++++---- pkg/providers/fallback_test.go | 2 +- pkg/tools/i2c_linux.go | 3 ++- pkg/tools/message_test.go | 2 +- pkg/tools/shell.go | 3 ++- 10 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pkg/channels/line.go b/pkg/channels/line.go index ffb5533e8..94b84d19d 100644 --- a/pkg/channels/line.go +++ b/pkg/channels/line.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -104,7 +105,7 @@ func (c *LINEChannel) Start(ctx context.Context) error { "addr": addr, "path": path, }) - if err := c.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err := c.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.ErrorCF("line", "Webhook server error", map[string]interface{}{ "error": err.Error(), }) diff --git a/pkg/providers/codex_cli_provider.go b/pkg/providers/codex_cli_provider.go index 8886406b4..7ebf3c91d 100644 --- a/pkg/providers/codex_cli_provider.go +++ b/pkg/providers/codex_cli_provider.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "os/exec" "strings" @@ -67,7 +68,7 @@ func (p *CodexCliProvider) Chat(ctx context.Context, messages []Message, tools [ } if err != nil { - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { return nil, ctx.Err() } if stderrStr := stderr.String(); stderrStr != "" { diff --git a/pkg/providers/codex_provider.go b/pkg/providers/codex_provider.go index e3526cfb5..a743d8c69 100644 --- a/pkg/providers/codex_provider.go +++ b/pkg/providers/codex_provider.go @@ -91,8 +91,8 @@ func (p *CodexProvider) Chat(ctx context.Context, messages []Message, tools []To if evt.Type == "response.completed" || evt.Type == "response.failed" || evt.Type == "response.incomplete" { evtResp := evt.Response if evtResp.ID != "" { - copy := evtResp - resp = © + cp := evtResp + resp = &cp } } } diff --git a/pkg/providers/error_classifier.go b/pkg/providers/error_classifier.go index a0f003006..97f954aa2 100644 --- a/pkg/providers/error_classifier.go +++ b/pkg/providers/error_classifier.go @@ -2,6 +2,7 @@ package providers import ( "context" + "errors" "regexp" "strings" ) @@ -102,12 +103,12 @@ func ClassifyError(err error, provider, model string) *FailoverError { } // Context cancellation: user abort, never fallback. - if err == context.Canceled { + if errors.Is(err, context.Canceled) { return nil } // Context deadline exceeded: treat as timeout, always fallback. - if err == context.DeadlineExceeded { + if errors.Is(err, context.DeadlineExceeded) { return &FailoverError{ Reason: FailoverTimeout, Provider: provider, diff --git a/pkg/providers/error_classifier_test.go b/pkg/providers/error_classifier_test.go index 865aea57a..d55585f55 100644 --- a/pkg/providers/error_classifier_test.go +++ b/pkg/providers/error_classifier_test.go @@ -293,7 +293,7 @@ func TestFailoverError_ErrorString(t *testing.T) { func TestFailoverError_Unwrap(t *testing.T) { inner := errors.New("inner error") fe := &FailoverError{Reason: FailoverTimeout, Wrapped: inner} - if fe.Unwrap() != inner { + if !errors.Is(inner, fe.Unwrap()) { t.Error("Unwrap should return wrapped error") } } diff --git a/pkg/providers/fallback.go b/pkg/providers/fallback.go index 9b07f9153..6b25da03a 100644 --- a/pkg/providers/fallback.go +++ b/pkg/providers/fallback.go @@ -2,6 +2,7 @@ package providers import ( "context" + "errors" "fmt" "strings" "time" @@ -98,7 +99,7 @@ func (fc *FallbackChain) Execute( for i, candidate := range candidates { // Check context before each attempt. - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { return nil, context.Canceled } @@ -130,7 +131,7 @@ func (fc *FallbackChain) Execute( } // Context cancellation: abort immediately, no fallback. - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { result.Attempts = append(result.Attempts, FallbackAttempt{ Provider: candidate.Provider, Model: candidate.Model, @@ -204,7 +205,7 @@ func (fc *FallbackChain) ExecuteImage( } for i, candidate := range candidates { - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { return nil, context.Canceled } @@ -219,7 +220,7 @@ func (fc *FallbackChain) ExecuteImage( return result, nil } - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { result.Attempts = append(result.Attempts, FallbackAttempt{ Provider: candidate.Provider, Model: candidate.Model, diff --git a/pkg/providers/fallback_test.go b/pkg/providers/fallback_test.go index ea81e0d48..c67e4b142 100644 --- a/pkg/providers/fallback_test.go +++ b/pkg/providers/fallback_test.go @@ -122,7 +122,7 @@ func TestFallback_ContextCanceled(t *testing.T) { } _, err := fc.Execute(ctx, candidates, run) - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { t.Errorf("expected context.Canceled, got %v", err) } } diff --git a/pkg/tools/i2c_linux.go b/pkg/tools/i2c_linux.go index 294f7ecbc..a12192c85 100644 --- a/pkg/tools/i2c_linux.go +++ b/pkg/tools/i2c_linux.go @@ -2,6 +2,7 @@ package tools import ( "encoding/json" + "errors" "fmt" "syscall" "unsafe" @@ -113,7 +114,7 @@ func (t *I2CTool) scan(args map[string]interface{}) *ToolResult { // Set slave address — EBUSY means a kernel driver owns this address _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSlave, uintptr(addr)) if errno != 0 { - if errno == syscall.EBUSY { + if errors.Is(errno, syscall.EBUSY) { found = append(found, deviceEntry{ Address: fmt.Sprintf("0x%02x", addr), Status: "busy (in use by kernel driver)", diff --git a/pkg/tools/message_test.go b/pkg/tools/message_test.go index 4bedbe79b..468f4aada 100644 --- a/pkg/tools/message_test.go +++ b/pkg/tools/message_test.go @@ -126,7 +126,7 @@ func TestMessageTool_Execute_SendFailure(t *testing.T) { if result.Err == nil { t.Error("Expected Err to be set") } - if result.Err != sendErr { + if !errors.Is(sendErr, result.Err) { t.Errorf("Expected Err to be sendErr, got %v", result.Err) } } diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index d9430672f..5e04b460d 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -3,6 +3,7 @@ package tools import ( "bytes" "context" + "errors" "fmt" "os" "os/exec" @@ -188,7 +189,7 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]interface{}) *To } if err != nil { - if cmdCtx.Err() == context.DeadlineExceeded { + if errors.Is(cmdCtx.Err(), context.DeadlineExceeded) { msg := fmt.Sprintf("Command timed out after %v", t.timeout) return &ToolResult{ ForLLM: msg,