nightshift: Error Message Improvement Audit
Repository: Microck/tailstick
Task: error-msg-improve
Category: options
Date: 2026-04-23
Summary
Audited all error messages across the Go codebase (8 packages, ~2000 lines of source). Found 50+ error creation sites. Good error wrapping in config and tailscale packages, but bare error propagation in crypto, state, and logging.
Findings by Package
internal/crypto/secret.go — 15 bare return err with no context
Severity: 🟡 MEDIUM
Encrypt() and Decrypt() have 15 return "", err statements with zero context. When encryption/decryption fails, the operator sees only the Go stdlib error (e.g., "crypto/cipher: invalid key size") with no indication it came from tailstick's secret handling.
Recommendation: Wrap each error site:
return "", fmt.Errorf("derive key: %w", err)
return "", fmt.Errorf("create cipher: %w", err)
return "", fmt.Errorf("generate nonce: %w", err)
return "", fmt.Errorf("marshal envelope: %w", err)
internal/state/store.go — 7 bare return err with no context
Severity: 🟡 MEDIUM
Only 2 sites have context ("read state", "parse state"). The other 7 return bare errors. State corruption issues are hard to diagnose without knowing which table/operation failed.
Recommendation: Wrap all error returns with operation context:
return fmt.Errorf("marshal state: %w", err)
return fmt.Errorf("write state file %s: %w", s.path, err)
internal/app/workflow.go — ~12 bare error returns
Severity: 🟡 MEDIUM
The Enroll function alone has ~10 error sites, most returning bare err. Enrollment is the primary user flow — errors need step context.
Recommendation: Wrap errors with the enrollment step that failed:
return model.LeaseRecord{}, fmt.Errorf("load config: %w", err)
return model.LeaseRecord{}, fmt.Errorf("resolve preset: %w", err)
return model.LeaseRecord{}, fmt.Errorf("validate lease duration: %w", err)
internal/logging/logger.go — 3 bare return err
Severity: 🟢 LOW
Recommendation: Add context: return nil, fmt.Errorf("create log directory: %w", err), etc.
internal/tailscale/client.go — Mostly good, 2 minor improvements
Severity: 🟢 LOW
Good messages (context included):
"no install command configured for platform %s" ✅
"stable channel requires configured stable version" ✅
"session mode requires ephemeral auth key" ✅
"missing auth key" ✅
Could improve:
- Line 119:
"status json missing Self object" → "parse tailscale status: response JSON missing required Self object"
- Line 258:
"timed lease requires days in {1,3,7}..." → include the actual invalid value: "...got %d"
internal/config/config.go — Excellent error messages ✅
All errors are descriptive and include context. No changes needed.
Error Message Quality Scorecard
| Package |
Total Sites |
Wrapped |
Bare |
Score |
config |
7 |
7 |
0 |
⭐⭐⭐⭐⭐ |
tailscale |
20 |
14 |
6 |
⭐⭐⭐⭐ |
gui |
3 |
2 |
1 |
⭐⭐⭐⭐ |
app/workflow |
~15 |
~3 |
~12 |
⭐⭐ |
state |
9 |
2 |
7 |
⭐⭐ |
crypto |
15 |
1 |
14 |
⭐ |
logging |
3 |
0 |
3 |
⭐ |
Recommended Priority
crypto/secret.go — 15 bare returns, most impactful for debugging secret handling failures
state/store.go — 7 bare returns, state corruption hard to diagnose without context
app/workflow.go — Enrollment is the primary user flow; errors need step context
logging/logger.go — 3 bare returns, low priority
tailscale/client.go — Minor improvements to 2 existing messages
nightshift: Error Message Improvement Audit
Repository: Microck/tailstick
Task: error-msg-improve
Category: options
Date: 2026-04-23
Summary
Audited all error messages across the Go codebase (8 packages, ~2000 lines of source). Found 50+ error creation sites. Good error wrapping in
configandtailscalepackages, but bare error propagation incrypto,state, andlogging.Findings by Package
internal/crypto/secret.go— 15 barereturn errwith no contextSeverity: 🟡 MEDIUM
Encrypt()andDecrypt()have 15return "", errstatements with zero context. When encryption/decryption fails, the operator sees only the Go stdlib error (e.g., "crypto/cipher: invalid key size") with no indication it came from tailstick's secret handling.Recommendation: Wrap each error site:
internal/state/store.go— 7 barereturn errwith no contextSeverity: 🟡 MEDIUM
Only 2 sites have context (
"read state","parse state"). The other 7 return bare errors. State corruption issues are hard to diagnose without knowing which table/operation failed.Recommendation: Wrap all error returns with operation context:
internal/app/workflow.go— ~12 bare error returnsSeverity: 🟡 MEDIUM
The
Enrollfunction alone has ~10 error sites, most returning bareerr. Enrollment is the primary user flow — errors need step context.Recommendation: Wrap errors with the enrollment step that failed:
internal/logging/logger.go— 3 barereturn errSeverity: 🟢 LOW
Recommendation: Add context:
return nil, fmt.Errorf("create log directory: %w", err), etc.internal/tailscale/client.go— Mostly good, 2 minor improvementsSeverity: 🟢 LOW
Good messages (context included):
"no install command configured for platform %s"✅"stable channel requires configured stable version"✅"session mode requires ephemeral auth key"✅"missing auth key"✅Could improve:
"status json missing Self object"→"parse tailscale status: response JSON missing required Self object""timed lease requires days in {1,3,7}..."→ include the actual invalid value:"...got %d"internal/config/config.go— Excellent error messages ✅All errors are descriptive and include context. No changes needed.
Error Message Quality Scorecard
configtailscaleguiapp/workflowstatecryptologgingRecommended Priority
crypto/secret.go— 15 bare returns, most impactful for debugging secret handling failuresstate/store.go— 7 bare returns, state corruption hard to diagnose without contextapp/workflow.go— Enrollment is the primary user flow; errors need step contextlogging/logger.go— 3 bare returns, low prioritytailscale/client.go— Minor improvements to 2 existing messages