narrow-bare-rescue
Narrow bare rescue in Elixir so real errors like KeyError and typos propagate instead of being swallowed. Use to audit rescues and refactor error ha…
它会碰到什么
这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。
技能内容
Narrow Bare Rescue
Turn rescue _ -> fallback into rescue _ in [ExceptionType1, ExceptionType2] -> fallback
so programmer bugs propagate while known failure modes stay handled.
Why this matters
Bare rescues (rescue _ ->, rescue e -> — any form without an in clause) swallow
every exception, including UndefinedFunctionError from typos, KeyError from
misspelled map keys, and CompileError from bad HEEx templates. The symptom isn't a stack
trace — it's a silent {:error, :generic} or a nil fallback. Bugs that should surface
in tests or error reporters become quiet degradations.
The Erlang Secure Coding Guide makes
the same case at the BEAM level — rule LNG-002 ("Do Not Use catch") warns that the
legacy catch-all form conflates normal returns, throws, and errors. Bare rescue in Elixir
is the direct analogue.
Iron Laws
- Never leave
rescue _ ->orrescue e ->without aninclause. Every rescue must
list exact exception types. The Credo check enforces this after cleanup lands.
- Cover every exception the code path can actually raise. Narrowing that drops a real
exception is a behavioral regression — trace each call in the body before committing.
- Never include programmer-bug exceptions in the list.
UndefinedFunctionError,
CompileError, BadFunctionError, and BadArityError must propagate.
- Use
reraise e, __STACKTRACE__, neverreraise e, []. Preserve the original stack
trace so Oban retry metadata and error reporters show the real origin.
- Run
mix compile --warnings-as-errorsbefore committing. Typos in exception module
names only surface at compile time — the code looks fine until it loads.
The core transform
# Before — masks programmer bugs
def parse(body) do
Jason.decode!(body)
rescue
_ -> %{}
end
# After — catches only what can actually fail here
def parse(body) do
Jason.decode!(body)
rescue
_ in [Jason.DecodeError, ArgumentError] -> %{}
end
Applies identically to try … rescue … and to function-body def … rescue ….
Workflow
The skill operates in three modes depending on scope:
- Single file —
/narrow-bare-rescue path/to/file.ex - Directory —
/narrow-bare-rescue lib/my_app/util/ - Whole project —
/narrow-bare-rescue --all
Whatever the scope, follow this sequence.
Step 1 — Find the sites
grep -rn "^\s*rescue\s*$" <scope> | head -200
For each hit, read the 3 lines after to classify:
rescue _ ->orrescue var ->— bare, needs narrowingrescue _ in [...] ->orrescue var in Something ->— already typed, skiprescue ExceptionType ->(no variable binding) — already typed, skip
Step 2 — Determine the exception set for each bare site
Read the try / def body and trace what each call can raise. Don't guess from the
function name — verify. Consult order:
- Check
${CLAUDE_SKILL_DIR}/references/taxonomy.mdfor the work type (JSON, Ecto, Money, HTTP, etc.).
Most sites map cleanly to one row.
- Grep deps for
defexceptionwhen a specific library isn't in the taxonomy:
grep -rn "defexception" deps/<libname>/lib/ | head -10
- Check
raisecalls in the code path itself — if the body explicitly raises
RuntimeError, include it.
Priorities: cover everything the code can actually raise, exclude programmer-bug
exceptions (see Iron Law #3), and prefer specific types (Jason.DecodeError beats
ArgumentError if both could apply).
Step 3 — Apply the narrowing
For files with ≥3 rescues sharing a taxonomy, hoist to a module attribute — see
${CLAUDE_SKILL_DIR}/references/patterns.md for the module-attribute pattern, Oban reraise, ExCmd exit
errors, and is_exception/1 replacements.
Step 4 — Verify
After changes in each file (or cluster of files), run:
mix compile --warnings-as-errors
mix format <files_changed>
mix test <test_files_for_affected_modules>
The compile step catches typos in exception module names — a real risk since you're writing
module names from memory.
Scope
This skill narrows bare rescue clauses. It does not:
- Auto-narrow blindly — behavior preservation matters; trace each call path first
- Touch rescues that are already typed (
rescue e in [X] ->) — those are correct - Cover
catchclauses — throws and exits from the process are a separate concern - Replace
try/rescuewithwithor error-tuple plumbing — that's a larger refactor
References
${CLAUDE_SKILL_DIR}/references/taxonomy.md— verified exception types per work
category, plus library-specific gotchas (NimbleCSV, Plug, Phoenix LiveView tokenizer)
${CLAUDE_SKILL_DIR}/references/patterns.md— special patterns:is_exception/1,
Oban reraise, ExCmd exit errors, module-attribute hoisting, partitioning large
cleanups, the regression-prevention Credo check
— BEAM-level rationale for preferring narrow try ... catch / try ... rescue over
the legacy catch-all form
想直接用这个技能?
本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。
它属于哪个仓库
plugins/elixir-phoenix/skills/narrow-bare-rescue/SKILL.md同一个仓库里的其他技能
同名技能的其他版本
有 6 个不同仓库或目录里都有叫 narrow-bare-rescue 的技能。它们内容并不相同,别混用:
- oliver-kriska/claude-elixir-phoenix — Narrow bare rescue in Elixir so real errors like KeyError and typos propagate
- oliver-kriska/claude-elixir-phoenix — Narrow bare rescue in Elixir so real errors like KeyError and typos;
- oliver-kriska/claude-elixir-phoenix — Narrow bare rescue in Elixir so real errors like KeyError and typos propagate
- oliver-kriska/claude-elixir-phoenix — Narrow bare rescue in Elixir so real errors like KeyError and typos propagate
- oliver-kriska/claude-elixir-phoenix — Narrow bare rescue in Elixir so real errors like KeyError and typos propagate