{"slug": "schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation", "title": "SchemaLinter-OneShot: Building a CLI Tool for Forcing LLM JSON Schema Validation and Self-Healing", "summary": "A developer built SchemaLinter-OneShot, a Python standard-library CLI tool that validates LLM JSON output against a schema and attempts self-healing of malformed responses. The project was closed as development incomplete after the author found that argparse flushes plain-text usage errors to stderr before a try/except SystemExit block can convert them into JSON, and that the same hook also swallows the exit code 0 from --help. The writeup documents the argparse lifecycle pitfalls as anti-patterns for future JSON-only CLI designs.", "body_md": "\"SchemaLinter-OneShot\" was designed as a lightweight, one-shot linter built entirely on the Python standard library, purposely eliminating excessive dependencies like heavy external validation libraries.\n\n`_extract_json`)` json ...`\nfenced code blocks.`{` to the last `}`.`_validate_types`)` jsonschema`, it recursively scans the payload to ensure the presence of required keys and performs minimal, strict type checking.\nThe implementation appeared beautifully cohesive. However, during the QA phase, when implementing the requirement to \"format error handling into JSON when arguments are missing,\" I fell into a deep quagmire caused by the internal specifications of the standard framework.\n\nWhen executing the script without arguments, instead of the intended JSON-formatted error, the default plain-text usage error from `argparse` leaked into the standard error stream.\n\n``` bash\n$ python3 V2_PROD_20261007_030033_test.py\nusage: V2_PROD_20261007_030033_test.py [-h] -s SCHEMA [-i INPUT]\n                                       [--mode {strict,prompt}]\nV2_PROD_20261007_030033_test.py: error: the following arguments are required: -s/--schema\n```\n\n💡 **For immediate deployment:** The complete source code suite (ZIP) for this architecture is available on [Gumroad](https://phenox.gumroad.com/l/lreerh) for $0+ (Pay What You Want).\n\nOn the development side, I took the approach of catching the error using a `try...except SystemExit:` block to intercept the termination and convert it into a JSON payload before exiting, as shown below:\n\n```\ntry:\n    args = parser.parse_args()\nexcept SystemExit:\n    print(json.dumps({\n        \"status\": \"error\", \n        \"message\": \"Argument parsing failed. Required argument '-s/--schema' is missing.\"\n    }, ensure_ascii=False))\n    sys.exit(2)\n```\n\nHowever, this approach contained **two fatal oversights regarding the lifecycle of the Python `argparse` module**:\n\n`argparse` detects missing required arguments or invalid options, it outputs a usage message directly to `sys.stderr` via its internal `error()` method `SystemExit`).` except` block to output the JSON payload, the plain-text error message had already been flushed to the standard error stream.`--help` (`-h`) with Validation Errors` SystemExit(0)` after successfully displaying the help message.`except SystemExit:` implementation above, Following the feedback from QA, I had to acknowledge the architectural limitations of relying on exception hooking within `argparse`. To build a truly robust JSON-only CLI, extensive refactoring would be required:\n\n`argparse.ArgumentParser` class.`sys.argv` prior to parsing to insert an explicit, decoupled validation layer upfront.\nHowever, the initial requirement for this tool was to be a **\"lightweight, one-shot tool that operates reliably within one second to ensure CI/CD pipeline health.\"** Continuously expending engineering effort on hacking standard frameworks at the primitive layer of CLI argument parsing would severely degrade the project's Return on Investment (ROI).\n\nMaking a comprehensive architectural judgment, I decided to avoid complicating the codebase with brittle patches in this version (V2). Consequently, I chose to temporarily **close the project as [Development Incomplete]**, extracting the technical debt and passing the accumulated knowledge on to future designs.\n\nThe architectural insights gained from this challenge will serve as valuable anti-patterns for future CLI tool development.\n\n*If this engineering log saved your production server (and your sanity), consider supporting our architecture on GitHub Sponsors.*", "url": "https://wpnews.pro/news/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation", "canonical_source": "https://dev.to/toai/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation-and-self-healing-3gla", "published_at": "2026-10-10 18:01:46+00:00", "updated_at": "2026-10-10 18:16:26.642199+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "large-language-models", "structured-data"], "entities": ["SchemaLinter-OneShot", "argparse", "Python", "Gumroad", "GitHub Sponsors"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation", "markdown": "https://wpnews.pro/news/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation.md", "text": "https://wpnews.pro/news/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation.txt", "jsonld": "https://wpnews.pro/news/schemalinter-oneshot-building-a-cli-tool-for-forcing-llm-json-schema-validation.jsonld"}}