Skip to content

Commit cdfe352

Browse files
author
Baton Admin
committed
chore: update connector skills via baton-admin
1 parent 6d6e3fb commit cdfe352

File tree

1 file changed

+311
-0
lines changed

1 file changed

+311
-0
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# review-common-bugs
2+
3+
Top bug patterns from production connector experience.
4+
5+
---
6+
7+
## #1: Entity Source Confusion (3 production reverts)
8+
9+
**The bug:** Getting workspace/org/tenant from entitlement instead of principal in Grant/Revoke.
10+
11+
```go
12+
// WRONG - caused production reverts
13+
func (g *groupBuilder) Grant(ctx context.Context, principal *v2.Resource,
14+
entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) {
15+
16+
// BUG: workspace from entitlement, not principal
17+
workspaceID := entitlement.Resource.ParentResourceId.Resource
18+
19+
// This grants in the WRONG workspace
20+
err := g.client.AddMember(ctx, workspaceID, groupID, userID)
21+
}
22+
```
23+
24+
**The fix:**
25+
```go
26+
// CORRECT
27+
workspaceID := principal.ParentResourceId.Resource // From principal
28+
```
29+
30+
**Why it happens:** Both principal and entitlement have `ParentResourceId`. Developers grab the wrong one.
31+
32+
**Detection:** Search for `entitlement.Resource.ParentResourceId` in Grant/Revoke methods.
33+
34+
---
35+
36+
## #2: Pagination Early Termination (most common)
37+
38+
**The bug:** Always returning empty next token, stopping after first page.
39+
40+
```go
41+
// WRONG - common copy-paste error
42+
func (u *userBuilder) List(ctx context.Context, parentID *v2.ResourceId,
43+
token *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
44+
45+
users, err := u.client.ListUsers(ctx)
46+
if err != nil {
47+
return nil, "", nil, err
48+
}
49+
50+
// BUG: Always returns "", even when more pages exist
51+
return resources, "", nil, nil
52+
}
53+
```
54+
55+
**The fix:**
56+
```go
57+
// CORRECT - use API's pagination info
58+
users, nextToken, err := u.client.ListUsers(ctx, currentToken)
59+
// ...
60+
return resources, nextToken, nil, nil // Pass through API's token
61+
```
62+
63+
**Why it happens:** Developers copy boilerplate and forget to wire up pagination.
64+
65+
**Detection:** Look for `return resources, "", nil, nil` with no conditional logic.
66+
67+
---
68+
69+
## #3: HTTP Response Nil Pointer (13 panic fixes)
70+
71+
**The bug:** Accessing `resp.Body` or `resp.StatusCode` when `resp` might be nil.
72+
73+
```go
74+
// WRONG - panics on network errors
75+
resp, err := client.Do(req)
76+
if err != nil {
77+
log.Printf("Error: %v, Status: %d", err, resp.StatusCode) // PANIC
78+
return err
79+
}
80+
defer resp.Body.Close()
81+
```
82+
83+
**The fix:**
84+
```go
85+
// CORRECT
86+
resp, err := client.Do(req)
87+
if err != nil {
88+
if resp != nil {
89+
defer resp.Body.Close()
90+
log.Printf("Error: %v, Status: %d", err, resp.StatusCode)
91+
}
92+
return fmt.Errorf("request failed: %w", err)
93+
}
94+
defer resp.Body.Close()
95+
```
96+
97+
**Why it happens:** On network errors (timeout, DNS failure), Go's http.Client returns error AND nil response.
98+
99+
**Detection:** Search for `resp.StatusCode` or `resp.Body` in error handling paths.
100+
101+
---
102+
103+
## #4: Type Assertion Panics
104+
105+
**The bug:** Direct type assertions without ok check.
106+
107+
```go
108+
// WRONG - panics if missing or wrong type
109+
userID := data["user_id"].(string)
110+
```
111+
112+
**The fix:**
113+
```go
114+
// CORRECT
115+
userID, ok := data["user_id"].(string)
116+
if !ok {
117+
return fmt.Errorf("user_id missing or not string")
118+
}
119+
```
120+
121+
**Why it happens:** Quick coding, assuming API always returns expected shape.
122+
123+
**Detection:** Regex `\.\([A-Za-z]+\)` without `ok` on same line.
124+
125+
---
126+
127+
## #5: Error Swallowing
128+
129+
**The bug:** Logging errors but continuing execution.
130+
131+
```go
132+
// WRONG - silent data loss
133+
users, err := client.ListUsers(ctx)
134+
if err != nil {
135+
log.Println("error listing users:", err)
136+
// Continues with empty users!
137+
}
138+
for _, user := range users {
139+
// ...
140+
}
141+
```
142+
143+
**The fix:**
144+
```go
145+
// CORRECT
146+
users, err := client.ListUsers(ctx)
147+
if err != nil {
148+
return nil, "", nil, fmt.Errorf("baton-myservice: failed to list users: %w", err)
149+
}
150+
```
151+
152+
**Why it happens:** Developers want to "handle" errors gracefully but create silent failures.
153+
154+
**Detection:** Look for `log.Print` followed by no return in error blocks.
155+
156+
---
157+
158+
## #6: Missing Error Prefix
159+
160+
**The bug:** Errors without connector name prefix.
161+
162+
```go
163+
// WRONG - hard to trace in logs
164+
return fmt.Errorf("failed to list users: %w", err)
165+
```
166+
167+
**The fix:**
168+
```go
169+
// CORRECT
170+
return fmt.Errorf("baton-myservice: failed to list users: %w", err)
171+
```
172+
173+
**Why it happens:** Copy-paste without updating prefix.
174+
175+
**Detection:** Grep for `fmt.Errorf("failed` without `baton-` prefix.
176+
177+
---
178+
179+
## #7: Wrong Error Verb (%v vs %w)
180+
181+
**The bug:** Using `%v` instead of `%w` breaks error chain.
182+
183+
```go
184+
// WRONG - breaks errors.Is() and errors.As()
185+
return fmt.Errorf("baton-myservice: failed: %v", err)
186+
```
187+
188+
**The fix:**
189+
```go
190+
// CORRECT - preserves error chain
191+
return fmt.Errorf("baton-myservice: failed: %w", err)
192+
```
193+
194+
**Why it happens:** `%v` is common for logging, `%w` is specific to error wrapping.
195+
196+
**Detection:** Search for `fmt.Errorf.*%v.*err` patterns.
197+
198+
---
199+
200+
## #8: Defer Before Error Check
201+
202+
**The bug:** Placing defer before checking if value is valid.
203+
204+
```go
205+
// WRONG - panics if resp is nil
206+
resp, err := client.Do(req)
207+
defer resp.Body.Close() // PANIC on error
208+
if err != nil {
209+
return err
210+
}
211+
```
212+
213+
**The fix:**
214+
```go
215+
// CORRECT
216+
resp, err := client.Do(req)
217+
if err != nil {
218+
return err
219+
}
220+
defer resp.Body.Close() // After error check
221+
```
222+
223+
**Why it happens:** Muscle memory from other languages, or copy-paste errors.
224+
225+
**Detection:** Look for `defer .*Close()` before `if err != nil`.
226+
227+
---
228+
229+
## #9: Unstable Resource IDs
230+
231+
**The bug:** Using mutable fields as resource ID.
232+
233+
```go
234+
// WRONG - email can change
235+
rs.NewUserResource(user.Name, userType, user.Email, ...)
236+
```
237+
238+
**The fix:**
239+
```go
240+
// CORRECT - use stable API ID
241+
rs.NewUserResource(user.Name, userType, user.ID, ...)
242+
```
243+
244+
**Why it happens:** Email seems like a good unique identifier, but it's mutable.
245+
246+
**Detection:** Look for `.Email` as third argument to `NewUserResource`.
247+
248+
---
249+
250+
## #10: Hardcoded API URLs
251+
252+
**The bug:** Hardcoding base URL prevents testing.
253+
254+
```go
255+
// WRONG - can't point at mock server
256+
const baseURL = "https://api.service.com"
257+
```
258+
259+
**The fix:**
260+
```go
261+
// CORRECT - configurable for testing
262+
var BaseURLField = field.StringField(
263+
"base-url",
264+
field.WithDescription("Override API base URL (for testing)"),
265+
field.WithDefaultValue("https://api.service.com"),
266+
)
267+
```
268+
269+
**Why it happens:** Developers focus on production, forget testing.
270+
271+
**Detection:** Look for `const.*URL` or `http://` / `https://` literals in client code.
272+
273+
---
274+
275+
## Bug Frequency Summary
276+
277+
| Bug | Frequency | Severity | Detection Difficulty |
278+
|-----|-----------|----------|---------------------|
279+
| Entity source confusion | Medium | Critical | Hard (logic error) |
280+
| Pagination termination | High | High | Easy (pattern match) |
281+
| HTTP nil pointer | High | Critical | Medium |
282+
| Type assertion panic | Medium | High | Easy (regex) |
283+
| Error swallowing | Medium | High | Medium |
284+
| Missing error prefix | High | Low | Easy |
285+
| Wrong error verb | Medium | Medium | Easy |
286+
| Defer before check | Low | Critical | Easy |
287+
| Unstable IDs | Low | High | Medium |
288+
| Hardcoded URLs | Medium | Low | Easy |
289+
290+
---
291+
292+
## Automated Detection
293+
294+
Add these to CI or code review automation:
295+
296+
```bash
297+
# Pagination termination
298+
grep -r 'return.*"",.*nil,.*nil' --include="*.go"
299+
300+
# HTTP nil pointer risk
301+
grep -r 'resp\.StatusCode\|resp\.Body' --include="*.go" | grep -v "if resp != nil"
302+
303+
# Missing error prefix
304+
grep -r 'fmt\.Errorf("failed' --include="*.go" | grep -v "baton-"
305+
306+
# Wrong error verb
307+
grep -r 'fmt\.Errorf.*%v.*err' --include="*.go"
308+
309+
# Type assertion without ok
310+
grep -rE '\.\([A-Za-z]+\)[^,]' --include="*.go" | grep -v ", ok :="
311+
```

0 commit comments

Comments
 (0)