You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(claude): tighten Error Messages; split examples into references doc
Replace the verbose Error Messages section with the junior-dev-tight
version from socket-repo-template: same four ingredients, same
audience-based length guidance, but rules are one-liners and only two
short example pairs remain inline.
Move worked examples and anti-patterns into
`docs/references/error-messages.md` so CLAUDE.md stays under the
40 KB always-loaded ceiling.
Copy file name to clipboardExpand all lines: CLAUDE.md
+20-22Lines changed: 20 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -90,37 +90,35 @@ The umbrella rule: never run a git command that mutates state belonging to a pat
90
90
91
91
## ERROR MESSAGES
92
92
93
-
Errors are a UX surface. Every error message must let the reader fix the problem without reading the source. Four ingredients, in order:
93
+
An error message is UI. The reader should be able to fix the problem from the message alone, without opening your source.
94
94
95
-
1.**What**: the rule that was violated (the contract, not the symptom)
96
-
2.**Where**: the exact file, key, line, or record — never "somewhere in config"
97
-
3.**Saw vs. wanted**: the offending value and the allowed shape/set
98
-
4.**Fix**: one concrete action to resolve it
95
+
Every message needs four ingredients, in order:
99
96
100
-
Find the balance between terse and meaningful — meaningful does not mean bloated:
97
+
1.**What** — the rule that was broken (e.g. "must be lowercase"), not the fallout ("invalid").
98
+
2.**Where** — the exact file, line, key, field, or CLI flag. Not "somewhere in config".
99
+
3.**Saw vs. wanted** — the bad value and the allowed shape or set.
100
+
4.**Fix** — one concrete action, in imperative voice (`rename the key to …`, not `the key was not renamed`).
101
101
102
-
-**Library-API errors** (thrown from the published package to its callers): terse. A caller catching and asserting on the message needs it short and stable. The four ingredients may collapse into one line; `name "__proto__" cannot start with an underscore` carries all four (what = start-rule, where = name component, saw = `__proto__`, fix implied by the rule).
103
-
-**Validator / config errors** (build-time, developer-facing): verbose. The reader is staring at a file, needs to find the offending record, and won't re-run the tool to spot the next hit. Every ingredient gets its own words.
104
-
-**Programmatic errors** (internal assertions, invariant checks): terse, rule-only. No caller will parse the message; terse keeps the check readable.
102
+
Length depends on the audience:
105
103
106
-
Baseline rules that apply to all three:
104
+
-**Library API errors** (thrown from a published package): terse. Callers may match on the message text, so every word counts. All four ingredients often fit in one sentence — e.g. `name "__proto__" cannot start with an underscore` covers rule, where (`name`), saw (`__proto__`), and implies the fix.
105
+
-**Validator / config / build-tool errors** (developer reading a terminal): verbose. Give each ingredient its own words so the reader can find the bad record without re-running the tool.
106
+
-**Programmatic errors** (internal assertions, invariant checks): terse, rule only. No end user will see it; short keeps the check readable.
107
107
108
-
- Write the fix step in the imperative (`add "filename" to part 3`), not passive narration (`"filename" was missing`).
109
-
- Never say "invalid" without what made it invalid. `invalid filename 'My Part'` is a symptom; `filename 'My Part' must be [a-z]+ (lowercase, no spaces)` is a rule.
110
-
- If two records collide, name both — not just the second one found.
111
-
- Suggest, don't auto-correct. An error that silently repairs state hides the bug in the next run.
112
-
- Bloat test: if removing a word loses information, keep it. If removing it loses only rhythm, drop it.
108
+
Rules for every message:
113
109
114
-
Example — validator on `tour.json` (verbose form, one record per run):
110
+
- Imperative voice for the fix — `add "filename" to part 3`, not `"filename" was missing`.
111
+
- Never "invalid" on its own. `invalid filename 'My Part'` is fallout; `filename 'My Part' must be [a-z]+ (lowercase, no spaces)` is a rule.
112
+
- On a collision, name **both** sides, not just the second one found.
113
+
- Suggest, don't auto-correct. Silently fixing state hides the bug next time.
114
+
- Bloat check: if removing a word keeps the information, drop it.
115
115
116
-
- ✗ `Error: invalid tour config`
117
-
- ✓ `tour.json: part 3 ("Parsing & Normalization") is missing "filename". Add a single-word lowercase filename (e.g. "parsing") to this part — one per part is required to route /<slug>/part/3 at publish time.`
116
+
Examples:
118
117
119
-
Example — library API error (terse form, caught in user code):
118
+
- ✗ `Error: invalid config` → ✓ `config.json: part 3 is missing "filename". Add a lowercase filename (e.g. "parsing").`
- ✗ `Error: invalid component` (misses what / saw)
122
-
- ✗ `The "name" component of type "npm" failed validation because the provided value "" is empty, which is not allowed because names are required; please provide a non-empty name.` (bloated — restates the rule three times)
123
-
- ✓ `npm "name" component is a required component` (rule + where + implicit saw=missing)
121
+
See `docs/references/error-messages.md` for worked examples and anti-patterns.
Companion to the `## Error Messages` section of `CLAUDE.md`. That section
4
+
holds the rules; this file holds longer examples and anti-patterns that
5
+
would bloat CLAUDE.md if inlined.
6
+
7
+
## The four ingredients
8
+
9
+
Every message needs, in order:
10
+
11
+
1.**What** — the rule that was broken.
12
+
2.**Where** — the exact file, line, key, field, or CLI flag.
13
+
3.**Saw vs. wanted** — the bad value and the allowed shape or set.
14
+
4.**Fix** — one concrete action, in imperative voice.
15
+
16
+
## Library API errors (terse)
17
+
18
+
Callers may match on the message text, so stability matters. Aim for one
19
+
sentence.
20
+
21
+
| ✗ / ✓ | Message | Notes |
22
+
| --- | --- | --- |
23
+
| ✗ |`Error: invalid component`| No rule, no saw, no where. |
24
+
| ✗ |`The "name" component of type "npm" failed validation because the provided value "" is empty, which is not allowed because names are required; please provide a non-empty name.`| Restates the rule three times. |
25
+
| ✓ |`npm "name" component is required`| Rule + where + implied saw (missing). Six words. |
26
+
| ✗ |`Error: bad name`| No rule. |
27
+
| ✓ |`name "__proto__" cannot start with an underscore`| Rule, where (`name`), saw (`__proto__`), fix implied. |
The reader is looking at a file and wants to fix the record without
32
+
re-running the tool. Give each ingredient its own words.
33
+
34
+
✗ `Error: invalid tour config`
35
+
36
+
✓ `tour.json: part 3 ("Parsing & Normalization") is missing "filename". Add a single-word lowercase filename (e.g. "parsing") to this part — one per part is required to route /<slug>/part/3 at publish time.`
37
+
38
+
Breakdown:
39
+
40
+
-**What**: `is missing "filename"` — the rule is "each part has a filename".
41
+
-**Where**: `tour.json: part 3 ("Parsing & Normalization")` — file + record + human label.
42
+
-**Saw vs. wanted**: saw = missing; wanted = a single-word lowercase filename, with `"parsing"` as a concrete model.
43
+
-**Fix**: `Add … to this part` — imperative, specific.
44
+
45
+
The trailing `to route /<slug>/part/3 at publish time` is optional. Include a *why* clause only when the rule is non-obvious; skip it for rules the reader already knows (e.g. "names can't start with an underscore").
46
+
47
+
## Programmatic errors (terse, rule only)
48
+
49
+
Internal assertions and invariant checks. No end user will read them;
50
+
terse keeps the assertion readable when you skim the code.
51
+
52
+
- ✓ `assert(queue.length > 0)` with message `queue drained before worker exit`
53
+
- ✓ `pool size must be positive`
54
+
- ✗ `An unexpected error occurred while trying to acquire a connection from the pool because the pool size was not positive.` — nothing a maintainer can act on that the rule itself doesn't already say.
55
+
56
+
## Common anti-patterns
57
+
58
+
**"Invalid X" with no rule.**
59
+
60
+
- ✗ `Invalid filename 'My Part'`
61
+
- ✓ `filename 'My Part' must be [a-z]+ (lowercase, no spaces)`
62
+
63
+
**Passive voice on the fix.**
64
+
65
+
- ✗ `"filename" was missing`
66
+
- ✓ `add "filename" to part 3`
67
+
68
+
**Naming only one side of a collision.**
69
+
70
+
- ✗ `duplicate key "foo"` (which record won, which lost?)
71
+
- ✓ `duplicate key "foo" in config.json (lines 12 and 47) — rename one`
72
+
73
+
**Silently auto-correcting.**
74
+
75
+
- ✗ Stripping a trailing slash from a URL and continuing. The next run will hit the same bug; nothing learned.
76
+
- ✓ `url "https://api/" has a trailing slash — remove it`.
77
+
78
+
**Bloat that restates the rule.**
79
+
80
+
- ✗ `The value provided for "timeout" is invalid because timeouts must be positive numbers and the value you provided was not a positive number.`
81
+
- ✓ `timeout must be a positive number (saw: -5)`
82
+
83
+
## Voice & tone
84
+
85
+
- Imperative for the fix: `rename`, `add`, `remove`, `set`.
86
+
- Present tense for the rule: `must be`, `cannot`, `is required`.
87
+
- No apology ("Sorry, …"), no blame ("You provided …"). State the rule and the fix.
88
+
- Don't end with "please"; it doesn't add information and it makes the message feel longer than it is.
89
+
90
+
## Bloat check
91
+
92
+
Before shipping a message, cross out any word that, if removed, leaves the information intact. If only rhythm or politeness disappears, drop it.
0 commit comments