fix: use errors.Is() for sentinel comparisons + remove unused @xyflow/react (#727)

Replace direct sentinel error comparisons (==, !=) with errors.Is()
across the codebase. Direct comparison breaks when errors are wrapped
with fmt.Errorf("...: %w", err), which can cause missed matches for
sql.ErrNoRows, io.EOF, context.DeadlineExceeded, and context.Canceled.

Affected packages:
- store/pg: sql.ErrNoRows in secure_cli, agents_export_team_*, mcp_export_queries
- store/sqlitestore: sql.ErrNoRows in schema migration
- tools: context.DeadlineExceeded in shell, credentialed_exec
- mcp: context.DeadlineExceeded in bridge_tool
- providers: context.Canceled in acp_provider, anthropic_stream_test
- updater: io.EOF in tar extraction

Also removes unused @xyflow/react dependency from web UI — it was
replaced by react-force-graph-2d but never cleaned up from package.json.

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
This commit is contained in:
Luan VuandLuvu182 authored and GitHub committed 2026-04-07 12:16:45 +07:00
1 parent 76385f2fc3
commit 0e2282be8f
13 files changed
+24 -105

No files matched your search

+2 -1
View File
@@ -2,6 +2,7 @@ package mcp
import (
"context"
"errors"
"fmt"
"strings"
"sync/atomic"
@@ -112,7 +113,7 @@ func (t *BridgeTool) Execute(ctx context.Context, args map[string]any) *tools.Re
result, err := t.client.CallTool(callCtx, req)
if err != nil {
if callCtx.Err() == context.DeadlineExceeded {
if errors.Is(callCtx.Err(), context.DeadlineExceeded) {
return tools.ErrorResult(fmt.Sprintf("MCP tool %q timeout after %ds", t.registeredName, t.timeoutSec))
}
return tools.ErrorResult(fmt.Sprintf("MCP tool %q error: %v", t.registeredName, err))
+2 -1
View File
@@ -2,6 +2,7 @@ package providers
import (
"context"
"errors"
"fmt"
"log/slog"
"regexp"
@@ -139,7 +140,7 @@ func (p *ACPProvider) ChatStream(ctx context.Context, req ChatRequest, onChunk f
defer cancelFn()
go func() {
<-cancelCtx.Done()
if ctx.Err() == context.Canceled {
if errors.Is(ctx.Err(), context.Canceled) {
_ = proc.Cancel()
}
}()
+2 -1
View File
@@ -2,6 +2,7 @@ package providers
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
@@ -71,7 +72,7 @@ func TestStreamChat_CancelledContext(t *testing.T) {
if err == nil {
t.Fatal("expected error from cancelled context, got nil")
}
if err != context.Canceled {
if !errors.Is(err, context.Canceled) {
// Accept either context.Canceled or context.DeadlineExceeded; the HTTP
// layer may wrap the error, so we check ctx.Err() as fallback.
if ctx.Err() == nil {
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"log/slog"
"github.com/google/uuid"
@@ -95,7 +96,7 @@ func ExportTeamByLead(ctx context.Context, db *sql.DB, agentID uuid.UUID) (*Team
" FROM agent_teams WHERE lead_agent_id = $1"+tc,
args...,
).Scan(&teamID, &t.Name, &t.Description, &t.Status, &t.Settings)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, uuid.Nil, nil, nil
}
if err != nil {
@@ -421,7 +422,7 @@ func ExportTeamPreviewCounts(ctx context.Context, db *sql.DB, agentID uuid.UUID)
err = db.QueryRowContext(ctx,
"SELECT id FROM agent_teams WHERE lead_agent_id = $1"+tc, args...,
).Scan(&teamID)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
err = nil
// Still count agent links even if no team
_ = db.QueryRowContext(ctx,
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"log/slog"
"github.com/google/uuid"
@@ -21,7 +22,7 @@ func ExportTeamByID(ctx context.Context, db *sql.DB, teamID uuid.UUID) (*TeamExp
" FROM agent_teams WHERE id = $1"+tc,
append([]any{teamID}, tcArgs...)...,
).Scan(&t.Name, &t.Description, &t.Status, &t.Settings)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
if err != nil {
@@ -162,7 +163,7 @@ func ExportTeamLeadAgentID(ctx context.Context, db *sql.DB, teamID uuid.UUID) (u
"SELECT lead_agent_id FROM agent_teams WHERE id = $1"+tc,
append([]any{teamID}, tcArgs...)...,
).Scan(&leadID)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return uuid.Nil, nil
}
return leadID, err
@@ -179,7 +180,7 @@ func ExportTeamAgentBasicInfo(ctx context.Context, db *sql.DB, agentID uuid.UUID
"SELECT agent_key FROM agents WHERE id = $1"+tc,
append([]any{agentID}, tcArgs...)...,
).Scan(&agentKey)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return "", nil
}
return agentKey, err
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"log/slog"
"github.com/google/uuid"
@@ -176,7 +177,7 @@ func ImportMCPServer(ctx context.Context, db *sql.DB, srv MCPServerExport, creat
if err == nil {
return existing, false, nil // already exists — return existing ID
}
if err != sql.ErrNoRows {
if !errors.Is(err, sql.ErrNoRows) {
return uuid.Nil, false, err
}
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"log/slog"
"time"
@@ -339,7 +340,7 @@ func (s *PGSecureCLIStore) scanRowWithGrantAndUserEnv(row *sql.Row) (*store.Secu
&userEnv,
)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
+2 -1
View File
@@ -5,6 +5,7 @@ package sqlitestore
import (
"database/sql"
_ "embed"
"errors"
"fmt"
"log/slog"
)
@@ -118,7 +119,7 @@ func EnsureSchema(db *sql.DB) error {
var current int
err := db.QueryRow("SELECT version FROM schema_version LIMIT 1").Scan(&current)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
// Fresh database — apply full schema.
slog.Info("sqlite: applying initial schema", "version", SchemaVersion)
tx, txErr := db.Begin()
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"maps"
@@ -259,7 +260,7 @@ func formatCredentialedResult(binary string, args []string,
}
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return ErrorResult(fmt.Sprintf("[CREDENTIALED EXEC] Command timed out after %s.\nBinary: %s", timeout, binary))
}
exitCode := -1
+1 -1
View File
@@ -328,7 +328,7 @@ func (t *ExecTool) executeOnHost(ctx context.Context, command, cwd string) *Resu
}
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return ErrorResult(fmt.Sprintf("command timed out after %s", t.timeout))
}
if result == "" {
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"archive/zip"
"compress/gzip"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
@@ -182,7 +183,7 @@ func applyMacOS(r io.Reader, tmpDir, appPath string) error {
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {
-1
View File
@@ -17,7 +17,6 @@
"@tailwindcss/typography": "^0.5.19",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-table": "^8.21.3",
"@xyflow/react": "^12.10.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"d3-force": "^3.0.0",
-90
View File
@@ -29,9 +29,6 @@ importers:
'@tanstack/react-table':
specifier: ^8.21.3
version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@xyflow/react':
specifier: ^12.10.2
version: 12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -1153,9 +1150,6 @@ packages:
'@types/d3-color@3.1.3':
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
'@types/d3-drag@3.0.7':
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
'@types/d3-ease@3.0.2':
resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
@@ -1171,9 +1165,6 @@ packages:
'@types/d3-scale@4.0.9':
resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
'@types/d3-selection@3.0.11':
resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
'@types/d3-shape@3.1.8':
resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
@@ -1183,12 +1174,6 @@ packages:
'@types/d3-timer@3.0.2':
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
'@types/d3-transition@3.0.9':
resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
'@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
@@ -1246,15 +1231,6 @@ packages:
babel-plugin-react-compiler:
optional: true
'@xyflow/react@12.10.2':
resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==}
peerDependencies:
react: '>=17'
react-dom: '>=17'
'@xyflow/system@0.0.76':
resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==}
accessor-fn@1.5.3:
resolution: {integrity: sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==}
engines: {node: '>=12'}
@@ -1291,9 +1267,6 @@ packages:
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
classcat@5.0.5:
resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@@ -2220,21 +2193,6 @@ packages:
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
zustand@4.5.7:
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
engines: {node: '>=12.7.0'}
peerDependencies:
'@types/react': '>=16.8'
immer: '>=9.0.6'
react: '>=16.8'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
zustand@5.0.12:
resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==}
engines: {node: '>=12.20.0'}
@@ -3267,10 +3225,6 @@ snapshots:
'@types/d3-color@3.1.3': {}
'@types/d3-drag@3.0.7':
dependencies:
'@types/d3-selection': 3.0.11
'@types/d3-ease@3.0.2': {}
'@types/d3-force@3.0.10': {}
@@ -3285,8 +3239,6 @@ snapshots:
dependencies:
'@types/d3-time': 3.0.4
'@types/d3-selection@3.0.11': {}
'@types/d3-shape@3.1.8':
dependencies:
'@types/d3-path': 3.1.1
@@ -3295,15 +3247,6 @@ snapshots:
'@types/d3-timer@3.0.2': {}
'@types/d3-transition@3.0.9':
dependencies:
'@types/d3-selection': 3.0.11
'@types/d3-zoom@3.0.8':
dependencies:
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
@@ -3353,29 +3296,6 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-rc.7
vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(jiti@2.6.1)
'@xyflow/react@12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@xyflow/system': 0.0.76
classcat: 5.0.5
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)
transitivePeerDependencies:
- '@types/react'
- immer
'@xyflow/system@0.0.76':
dependencies:
'@types/d3-drag': 3.0.7
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
'@types/d3-transition': 3.0.9
'@types/d3-zoom': 3.0.8
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-zoom: 3.0.0
accessor-fn@1.5.3: {}
aria-hidden@1.2.6:
@@ -3404,8 +3324,6 @@ snapshots:
dependencies:
clsx: 2.1.1
classcat@5.0.5: {}
clsx@2.1.1: {}
comma-separated-tokens@2.0.3: {}
@@ -4583,14 +4501,6 @@ snapshots:
zod@4.3.6: {}
zustand@4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4):
dependencies:
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
'@types/react': 19.2.14
immer: 11.1.4
react: 19.2.4
zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)):
optionalDependencies:
'@types/react': 19.2.14