{"slug":"narrow-bare-rescue-2","title":"narrow-bare-rescue","summary":"Narrow bare rescue in Elixir so real errors like KeyError and typos propagate","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-10-04T15:14:22.776336Z","repo":{"url":"https://github.com/oliver-kriska/claude-elixir-phoenix","stars":560,"forks":44,"license":"MIT","updatedAt":"2026-10-02T04:11:38Z"},"bodyHtml":"<hr>\n<h2>name: narrow-bare-rescue\ndescription: Narrow bare rescue in Elixir so real errors like KeyError and typos propagate\ninstead of being swallowed. Use to audit rescues and refactor error handling.</h2>\n<h1>Narrow Bare Rescue</h1>\n<p>Turn <code>rescue _ -&gt; fallback</code> into <code>rescue _ in [ExceptionType1, ExceptionType2] -&gt; fallback</code>\nso programmer bugs propagate while known failure modes stay handled.</p>\n<h2>Why this matters</h2>\n<p>Bare rescues (<code>rescue _ -&gt;</code>, <code>rescue e -&gt;</code> — any form without an <code>in</code> clause) swallow\n<strong>every</strong> exception, including <code>UndefinedFunctionError</code> from typos, <code>KeyError</code> from\nmisspelled map keys, and <code>CompileError</code> from bad HEEx templates. The symptom isn't a stack\ntrace — it's a silent <code>{:error, :generic}</code> or a <code>nil</code> fallback. Bugs that should surface\nin tests or error reporters become quiet degradations.</p>\n<p>The Erlang <a href=\"https://www.erlang.org/doc/system/secure_coding.html\">Secure Coding Guide</a> makes\nthe same case at the BEAM level — rule <strong>LNG-002</strong> (\"Do Not Use <code>catch</code>\") warns that the\nlegacy catch-all form conflates normal returns, throws, and errors. Bare <code>rescue</code> in Elixir\nis the direct analogue.</p>\n<h2>Iron Laws</h2>\n<ol>\n<li><strong>Never leave <code>rescue _ -&gt;</code> or <code>rescue e -&gt;</code> without an <code>in</code> clause.</strong> Every rescue must\nlist exact exception types. The Credo check enforces this after cleanup lands.</li>\n<li><strong>Cover every exception the code path can actually raise.</strong> Narrowing that drops a real\nexception is a behavioral regression — trace each call in the body before committing.</li>\n<li><strong>Never include programmer-bug exceptions in the list.</strong> <code>UndefinedFunctionError</code>,\n<code>CompileError</code>, <code>BadFunctionError</code>, and <code>BadArityError</code> must propagate.</li>\n<li><strong>Use <code>reraise e, __STACKTRACE__</code>, never <code>reraise e, []</code>.</strong> Preserve the original stack\ntrace so Oban retry metadata and error reporters show the real origin.</li>\n<li><strong>Run <code>mix compile --warnings-as-errors</code> before committing.</strong> Typos in exception module\nnames only surface at compile time — the code looks fine until it loads.</li>\n</ol>\n<h2>The core transform</h2>\n<pre><code># Before — masks programmer bugs\ndef parse(body) do\n  Jason.decode!(body)\nrescue\n  _ -&gt; %{}\nend\n\n# After — catches only what can actually fail here\ndef parse(body) do\n  Jason.decode!(body)\nrescue\n  _ in [Jason.DecodeError, ArgumentError] -&gt; %{}\nend\n</code></pre>\n<p>Applies identically to <code>try … rescue …</code> and to function-body <code>def … rescue …</code>.</p>\n<h2>Workflow</h2>\n<p>The skill operates in three modes depending on scope:</p>\n<ol>\n<li><strong>Single file</strong> — <code>/narrow-bare-rescue path/to/file.ex</code></li>\n<li><strong>Directory</strong> — <code>/narrow-bare-rescue lib/my_app/util/</code></li>\n<li><strong>Whole project</strong> — <code>/narrow-bare-rescue --all</code></li>\n</ol>\n<p>Whatever the scope, follow this sequence.</p>\n<h3>Step 1 — Find the sites</h3>\n<pre><code>grep -rn \"^\\s*rescue\\s*$\" &lt;scope&gt; | head -200\n</code></pre>\n<p>For each hit, read the 3 lines after to classify:</p>\n<ul>\n<li><code>rescue _ -&gt;</code> or <code>rescue var -&gt;</code> — bare, needs narrowing</li>\n<li><code>rescue _ in [...] -&gt;</code> or <code>rescue var in Something -&gt;</code> — already typed, skip</li>\n<li><code>rescue ExceptionType -&gt;</code> (no variable binding) — already typed, skip</li>\n</ul>\n<h3>Step 2 — Determine the exception set for each bare site</h3>\n<p>Read the <code>try</code> / <code>def</code> body and trace what each call can raise. Don't guess from the\nfunction name — verify. Consult order:</p>\n<ol>\n<li><p><strong>Check <code>references/taxonomy.md</code></strong> for the work type (JSON, Ecto, Money, HTTP, etc.).\nMost sites map cleanly to one row.</p>\n</li>\n<li><p><strong>Grep deps for <code>defexception</code></strong> when a specific library isn't in the taxonomy:</p>\n<pre><code>grep -rn \"defexception\" deps/&lt;libname&gt;/lib/ | head -10\n</code></pre>\n</li>\n<li><p><strong>Check <code>raise</code> calls in the code path itself</strong> — if the body explicitly raises\n<code>RuntimeError</code>, include it.</p>\n</li>\n</ol>\n<p>Priorities: cover everything the code can actually raise, exclude programmer-bug\nexceptions (see Iron Law #3), and prefer specific types (<code>Jason.DecodeError</code> beats\n<code>ArgumentError</code> if both could apply).</p>\n<h3>Step 3 — Apply the narrowing</h3>\n<p>For files with ≥3 rescues sharing a taxonomy, hoist to a module attribute — see\n<code>references/patterns.md</code> for the module-attribute pattern, Oban reraise, ExCmd exit\nerrors, and <code>is_exception/1</code> replacements.</p>\n<h3>Step 4 — Verify</h3>\n<p>After changes in each file (or cluster of files), run:</p>\n<pre><code>mix compile --warnings-as-errors\nmix format &lt;files_changed&gt;\nmix test &lt;test_files_for_affected_modules&gt;\n</code></pre>\n<p>The compile step catches typos in exception module names — a real risk since you're writing\nmodule names from memory.</p>\n<h2>Scope</h2>\n<p>This skill narrows bare <code>rescue</code> clauses. It does not:</p>\n<ul>\n<li>Auto-narrow blindly — behavior preservation matters; trace each call path first</li>\n<li>Touch rescues that are already typed (<code>rescue e in [X] -&gt;</code>) — those are correct</li>\n<li>Cover <code>catch</code> clauses — throws and exits from the process are a separate concern</li>\n<li>Replace <code>try/rescue</code> with <code>with</code> or error-tuple plumbing — that's a larger refactor</li>\n</ul>\n<h2>References</h2>\n<ul>\n<li><code>references/taxonomy.md</code> — verified exception types per work\ncategory, plus library-specific gotchas (NimbleCSV, Plug, Phoenix LiveView tokenizer)</li>\n<li><code>references/patterns.md</code> — special patterns: <code>is_exception/1</code>,\nOban reraise, ExCmd exit errors, module-attribute hoisting, partitioning large\ncleanups, the regression-prevention Credo check</li>\n<li><a href=\"https://www.erlang.org/doc/system/secure_coding.html\">Erlang Secure Coding Guide — LNG-002: Do Not Use <code>catch</code></a>\n— BEAM-level rationale for preferring narrow <code>try ... catch</code> / <code>try ... rescue</code> over\nthe legacy catch-all form</li>\n</ul>\n","files":[{"path":"references/patterns.md","sizeBytes":3906,"isText":true},{"path":"references/taxonomy.md","sizeBytes":10602,"isText":true},{"path":"SKILL.md","sizeBytes":5380,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-10-04T15:16:16.490882Z","sha256":"0FC879B40ABDD4CF45129462955B7B2538F92F7C27918AC0749DC4A2183E59EE","sizeBytes":8731},"review":null,"source":{"repositoryUrl":"https://github.com/oliver-kriska/claude-elixir-phoenix","path":"targets/amp/skills/narrow-bare-rescue","license":"MIT","commit":"9767a82d24ddddad553e85f88efc2869a7fd7d88","subtreeSha":"9FDF682192982F78EB5994CBA086437F23D33DA711D5275F0A199F77871950D7","lastSyncedAt":"2026-10-04T15:14:09.139242Z"},"reviewedAt":"2026-10-04T15:19:37.901883Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/targets/amp/skills/narrow-bare-rescue"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart"},{"target":"git","command":"git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git"}]}