Json decode no handling
Skill viditkbhatnagar/immunize/src/immunize/patterns/json-decode-no-handling
A curated pattern library that stops AI coding assistants from repeating common runtime errors. No API key. No LLM calls at runtime.
npx -y skills add viditkbhatnagar/immunize --skill json-decode-no-handlingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 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
Use when writing Python that parses JSON from outside the process — an HTTP response, a file, an environment value — to ensure JSONDecodeError is caught and translated into a typed module exception, not leaked to callers.
SKILL.md
3.1 KB, as published. Nobody here has run it
json-decode-no-handling
When the JSON came from outside the process, parsing it can fail. An
upstream service returns an HTML error page instead of JSON. A file is
truncated. An environment variable holds a leftover Python repr. An AI
that never wraps json.loads lets the raw decoder exception escape:
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Catch the decoder exception at the same boundary you catch other I/O
errors, and translate it into your module's own typed exception. Don't
leak json.JSONDecodeError across an API surface — callers shouldn't
have to import json to handle bad input from somewhere else.
Example
Wrong — a malformed payload crashes with the raw decoder error:
import json
def parse_config(text: str) -> dict:
return json.loads(text)
Right — wrap, translate, preserve the cause:
import json
class ConfigError(Exception):
pass
def parse_config(text: str) -> dict:
try:
return json.loads(text)
except json.JSONDecodeError as exc:
raise ConfigError(
f"invalid JSON config at line {exc.lineno}, "
f"column {exc.colno}: {exc.msg}"
) from exc
raise ... from exc keeps the original traceback chained so callers
debugging in a REPL still see the underlying decoder message.
When to NOT wrap
If the JSON is generated inside this process and a decode failure
genuinely is a programmer bug, let it crash. Wrapping always-trusted
inputs hides bugs. The rule is: wrap inputs from the file system,
network, environment, or user; don't wrap your own json.dumps output
fed back through json.loads in the same module.
response.json() is the same trap
requests.Response.json() and httpx.Response.json() raise
json.JSONDecodeError (or a thin wrapper) on non-JSON bodies — the
same failure shape as json.loads. The same try/except rule
applies.
import json
import requests
def fetch_user(user_id: str) -> dict:
response = requests.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status()
try:
return response.json()
except json.JSONDecodeError as exc:
raise UpstreamError(f"user API returned non-JSON: {exc.msg}") from exc
Don't catch Exception
Bare except: or except Exception: swallows real bugs (typos,
attribute errors, KeyboardInterrupt). Catch json.JSONDecodeError
specifically. If you also need to handle I/O errors at the same call
site, list them — except (json.JSONDecodeError, OSError) as exc: —
not except Exception:.
Immunity note
The verification calls parse_config with a truncated string and
asserts the raised exception is not a json.JSONDecodeError. The
repro re-raises the decoder error directly and fails the assertion;
the fix raises a typed ConfigError and passes.