agentsclimarketplace

Error explainer

Skill andy-builds-ai/claude-code-skills/error-explainer

A small collection of Claude Code skills for Python development.

Install
npx -y skills add andy-builds-ai/claude-code-skills --skill error-explainer

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

When reading a Python traceback. Work from the bottom up, understand the mechanics behind the error, and only then look for the fix.

SKILL.md

13.9 KB, as published. Nobody here has run it

error-explainer

When this skill triggers

As soon as a Python traceback appears in the output — whether in a learning exercise, a learning script, an assignment, or a production project. The skill triggers the moment Python throws an error and the user reads the traceback.

Not active when the code runs but returns the wrong result — that belongs to debug-walkthrough. The trigger here is binary: is there a traceback in the output, yes or no.

Two modes — context decides

The skill works in two modes with a clearly separated default rule.

Learning mode (default): For every traceback that comes up in learning exercises, learning scripts, test scripts, assignments, or while learning Python in general. Here the junior is guided through the traceback: first explain the mechanics behind it, then work out the fix together — don't hand it over.

Tool mode: For tracebacks from production projects, or when the user explicitly says "tool mode". Here the diagnosis and the proposed fix come straight away — learning isn't the goal, getting on with it is.

One mode is always active. When it's unclear which one fits, assume learning mode — it's the safe default for a junior profile.

Reading a traceback — method

Tracebacks are read from the bottom up. That's not habit, it's mechanics: Python writes the traceback in call order, so the first call is at the top and the crash is at the bottom. The bottom line is always the error line with the error type.

Three questions while reading, in this order:

  1. What's in the bottom line? That's the error type plus the error message. Without reading this line, everything else is guessing.
  2. In which file and line did it happen? It's right above the error line, indented with File "...". That's where it happened.
  3. How was this line called? It's in the lines above. That's the call path — important when the error happens in a function that was called from somewhere else.

The call-path lines often get skipped. For simple scripts that's fine. With several functions calling each other, the call path is the most important part — it shows which function was called with which values.

Common error types with their mechanics

Seven types that together cover the vast majority of all tracebacks you'll see while learning and in production projects.

NameError

NameError: name 'x' is not defined

What happens: at the moment the line ran, Python found no name x in any scope. Python searches in this order: local scope (inside the current function), enclosing scopes (with nested functions), global scope (module level), built-in names (print, len, etc). If the name exists in none of these four scopes, you get the NameError.

Common causes: a typo in the variable name. The variable was defined, but in another function. The variable was defined inside an if block that didn't run. A forgotten import.

What next: which line does it happen in, where was the name last set — if at all.

TypeError

TypeError: can only concatenate str (not "int") to str
TypeError: 'NoneType' object is not subscriptable
TypeError: missing 1 required positional argument: 'name'

What happens: Python tried an operation that isn't defined for the given types. String plus integer doesn't work without a conversion. Accessing None with [...] doesn't work because None has no indices. Calling a function without a required argument doesn't work.

Mechanics: Python checks types at run time, not while writing. So the code often looks correct until, at that moment, Python notices: "I can't run this operation on this type". The error message almost always says which operation on which type was refused — it's more talkative than with other types.

Common causes: a function returned None where something else was expected. User input is a string where a number was expected (input() always returns a string). Arguments passed in the wrong order.

What next: which variable has which type at that moment? A print(type(variable)) right before the crash line shows it.

KeyError

KeyError: 'blocks'

What happens: you accessed a key in a dictionary that isn't there. Python throws the missing key as the error message.

Mechanics: dictionary access with dict["key"] is a hard operation — if the key is missing, crash. By contrast, dict.get("key") returns None for a missing key, and dict.get("key", "default") returns a default value.

Common causes: a typo in the key name. A response from an API has a different structure than expected (an API doesn't always return the same fields across versions, for example). The key written with different casing ("Blocks" instead of "blocks").

What next: which keys does the dict actually have? A print(list(my_dict.keys())) right before shows it.

AttributeError

AttributeError: 'NoneType' object has no attribute 'split'
AttributeError: 'list' object has no attribute 'keys'

What happens: you called a method or accessed an attribute on an object that doesn't exist on that type. None.split() doesn't work because None has no split method. [1,2,3].keys() doesn't work because lists have no keys method (dictionaries do).

Mechanics: every type in Python has a fixed set of methods and attributes. On the call, Python checks whether the name exists on the object — if not, AttributeError. The error message always names the type and the missing name.

Common causes: a function returned None instead of the expected object. The variable holds a different type than you thought (a list instead of a dict, or the other way around). A typo in the method name (appned instead of append).

What next: what type is the object really? print(type(variable)) right before the crash line.

ValueError

ValueError: invalid literal for int() with base 10: 'abc'
ValueError: not enough values to unpack (expected 3, got 2)

What happens: the type is right, but the value is invalid for the operation. int("abc") fails because "abc" isn't a valid integer string — the type is a string, which int() expects, only the value doesn't fit. With a, b, c = [1, 2] the list is the wrong value because it has two elements instead of three.

Mechanics: the difference from TypeError matters. TypeError = the type is wrong. ValueError = the type is right, the value is wrong. This distinction helps when debugging — when a ValueError comes up, you don't check the type but the actual value.

Common causes: converting a string to a number without validating first. Tuple unpacking with the wrong number of elements. Functions with strict format requirements (datetime.strptime with a mismatched format string).

What next: what actual value does the variable have at that moment? print(repr(variable)) shows the value with quotes — useful for spotting spaces or special characters.

IndentationError

IndentationError: expected an indented block
IndentationError: unexpected indent

What happens: Python found no consistent indentation where it expected one — or found one where none is allowed. Unlike all the other errors here, this is a syntax error — Python can't even run the code, the crash happens during parsing.

Mechanics: Python uses indentation as code structure instead of curly braces. After an if, for, def, class, etc. the next block has to be indented. Mixing tabs and spaces is a common hidden trigger — it looks the same in the editor but is different to Python.

Common causes: forgotten indentation after def, if, for. An accidentally indented line at the start of a file. The editor mixing tabs and spaces (VS Code does show this if you turn on the "Render Whitespace" setting).

What next: which line? Make whitespace visible in the editor. If you suspect a tab/space mix, clean the file up with "Convert Indentation to Spaces".

ImportError / ModuleNotFoundError

ModuleNotFoundError: No module named 'anthropic'
ImportError: cannot import name 'foo' from 'bar'

What happens: Python can't load a module or a name from a module. With ModuleNotFoundError the whole package is missing. With ImportError the package exists but the name you want isn't in it.

Mechanics: Python looks for modules in a fixed list of directories (sys.path) — the current file, the working directory, installed packages in site-packages. If the module isn't in any of these directories, crash.

Common causes: the package isn't installed (pip install anthropic forgotten). The wrong virtual environment is active. A typo in the module name. With optional dependencies: a lazy import wasn't used and a test ran without the optional library.

What next: is the package installed in the current environment? pip list | grep packagename shows it. If it isn't installed: install it or use a lazy import.

Pattern for optional dependencies: for optional libraries (anthropic, ollama-client, etc.) move the import into the function that uses it, not at the top of the module:

def call_anthropic(prompt):
    from anthropic import Anthropic
    client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
    # ...

That way the module loads even without the optional library installed — the ImportError only comes when the function is actually called, not already when the module is imported. Tests that should run without this library keep working.

Learning mode — how the skill guides the junior

In learning mode the fix isn't handed over. The junior works through the traceback. Three steps:

  1. Make the mechanics clear — what kind of error type is this, what does Python do internally. The section above gives that for the seven most common types.
  2. Ask a diagnostic question, don't give the diagnosis — "what type does the variable have at that moment?" instead of "the variable is None". The junior should look for themselves with a print(type(...)) or print(repr(...)).
  3. The fix only once the junior has understood the cause — when they say "ah, the function returned None because the RPC call failed", only then talk about the correction.

This is slower than just offering the fix. But the junior won't have to ask again on the next error of the same kind — they've understood the mechanics.

Tool mode — how the skill delivers directly

In tool mode it's about speed. Three steps:

  1. Quote the bottom line — error type plus message.
  2. Direct diagnosis — "variable X is None on line Y because function Z returns None on an API error".
  3. Proposed fix — a concrete code suggestion or a pointer to a known issue.

No diagnostic questions, no explanation of the mechanics — the user knows the mechanics, they just want the bug gone.

Gotchas

Scrolling past the traceback without reading it. Tracebacks look intimidating — many lines, many paths. The temptation: scroll down and go straight to looking for a fix. What happens: the error line gets missed or confused with one of the call-path lines. How to avoid: every traceback gets read from the bottom up, always. First the bottom line, then the file line above it, then the call path.

Fixing the symptom instead of the cause. The crash says "X is None". Reflex: if X is None: return. What happens: the crash is gone, but the real question was never asked — why was X None? The bug stays hidden and comes back elsewhere. How to avoid: on every crash ask "why does this value arrive here?", not "how do I prevent the crash?".

Confusing the error type with the error message. The junior reads "TypeError: 'NoneType' object is not subscriptable" and thinks the error is called "NoneType". No — the error is called TypeError, the message describes the specific case. How to avoid: on every traceback identify the type first (left of the colon), then the message.

Stack Overflow or ChatGPT before reading. The temptation: copy the error message, google it, take the first answer. What happens: often the solution from the internet fits a similar but not identical case. The code gets "fixed" but doesn't work or has a new bug. How to avoid: understand the traceback in your own context first, then look things up if the mechanics aren't clear. The five minutes of reading often save half an hour of hunting afterward.

Ignoring the call path with nested functions. A crash in a small helper function. The junior looks only there and sees no error in the function itself. What happens: the function is correct, the problem is in the caller that passes wrong values. How to avoid: for every traceback with more than two lines of call path, look at all the callers too — the bug is often one level up.

Example run

The junior runs a learning exercise and gets this output:

Traceback (most recent call last):
  File "example.py", line 12, in <module>
    print(numbers[5])
IndexError: list index out of range

The skill triggers, learning mode (learning exercise recognized):

Make the mechanics clear: "That's an IndexError. On a list access Python checks whether the index exists — that is, whether the position is in the range 0 to length-1. If the index is out of range, crash. Position 5 would mean a sixth element — but the list has fewer."

Diagnostic question: "Look with print(len(numbers)) right before line 12 — how many elements does the list actually have at that moment?"

The junior answers: "There are 4 elements."

Work out the fix together: "Right. The list has 4 elements, so indices 0 to 3. Position 5 doesn't exist. What did you actually want to read — the last element? Then numbers[-1] is the way, or numbers[len(numbers)-1]."

End result: the junior has understood why the error came up, now knows two ways to get to the last element, and won't be stuck on the next IndexError.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.