[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-en-1-1-all-golang-the-series-ep168-error-handling-in-ai-all--*":3,"academy-blog-translations-54u71ct4gosq2pv":92},{"data":4,"page":77,"perPage":77,"totalItems":77,"totalPages":77},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":85,"keywords":86,"locale":57,"published_at":87,"scheduled_at":73,"school_blog":81,"short_description":88,"status":79,"title":89,"updated":90,"updated_by":91,"slug":82,"views":84},"Golang code snippet for handling AI API errors and parsing malformed JSON","sclblg987654321","school_blog_translations","\u003Cp>Welcome to EP.168! In the previous episode (\u003Ca rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.superdevacademy.com\u002Fblogs\u002Fgolang-the-series-ep167-monitoring-ai-performance\">EP.167: Monitoring AI Performance\u003C\u002Fa>), we set up a monitoring system using Prometheus to measure our system's latency. Today, we're going to tackle one of the biggest pain points that every AI Backend developer has likely encountered: malformed JSON or AI models returning conversational text instead of the structured data we requested.\u003C\u002Fp>\u003Cp>When writing a standard Web API, if something goes wrong, the server usually spits out an HTTP Status Code immediately, such as \u003Cstrong>400 Bad Request\u003C\u002Fstrong> or \u003Cstrong>500 Internal Server Error\u003C\u002Fstrong>. However, the world of Large Language Models (LLMs) isn't that straightforward. We often face headache-inducing cases like:\u003C\u002Fp>\u003Col>\u003Cli>\u003Cp>\u003Cstrong>HTTP Status 200 OK, but the payload is a Refusal:\u003C\u002Fstrong> The model replies with, \u003Cem>\"I'm sorry, but I cannot answer this question due to safety policies...\"\u003C\u002Fem>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>Invalid Structured Output (Malformed JSON):\u003C\u002Fstrong> We prompt the model to return JSON so we can use \u003Ccode>json.Unmarshal\u003C\u002Fcode> into a Go Struct, but the model throws in a Markdown Code Block (\u003Ccode>json ... \u003C\u002Fcode>) or forgets to close a brace \u003Ccode>}\u003C\u002Fcode>, causing the system to crash.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>Hallucinations &amp; Out-of-Bounds Values:\u003C\u002Fstrong> The model returns correctly formatted data, but the values violate system constraints (e.g., a Score should be between 0.0 - 1.0, but the model sends 99.0).\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Fol>\u003Cp>If we let this unexpected data slip through into our system, it will cause a Panic or immediately break the Business Logic on the Go side. In this episode, we'll look at techniques for catching and handling these errors in Go.\u003C\u002Fp>\u003Ch2>3-Layer Structure for AI Error Handling\u003C\u002Fh2>\u003Cp>To ensure safety, we will implement a 3-layer defense structure:\u003C\u002Fp>\u003Cp>Plaintext\u003C\u002Fp>\u003Cpre>\u003Ccode>[User Input]\n       │\n       ▼\n┌─────────────────────────────────┐\n│ 1. Pre-validation &amp; Guardrails  │ ───&gt; Catch Prompt Injection \u002F Out-of-scope questions before sending\n└────────────────┬────────────────┘\n                 │\n                 ▼\n          [Call LLM API]\n                 │\n                 ▼\n┌─────────────────────────────────┐\n│ 2. Structural Parsing &amp; Retry   │ ───&gt; Clean up strings &amp; Catch Malformed JSON\n└────────────────┬────────────────┘\n                 │\n                 ▼\n┌─────────────────────────────────┐\n│ 3. Semantic Validation          │ ───&gt; Verify Business Rules and numerical bounds\n└────────────────┬────────────────┘\n                 │\n                 ▼\n         [Safe Business Logic]\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>Structure and Go Code for Error Handling with Retry Loop\u003C\u002Fh2>\u003Cp>To deal with models spitting out broken JSON formats, we'll build functions to clean up the text, collect errors, and automatically trigger a Retry Loop if parsing fails.\u003C\u002Fp>\u003Ch3>Part 1: Data Structures, Clean Up Function, and Semantic Validator\u003C\u002Fh3>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package main\n\nimport (\n\t\"context\"\n\t\"encoding\u002Fjson\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\n\u002F\u002F UserAnalysisResult is the target struct we want to receive from the AI\ntype UserAnalysisResult struct {\n\tSentiment string   `json:\"sentiment\"`\n\tKeywords  []string `json:\"keywords\"`\n\tScore     float64  `json:\"score\"`\n}\n\n\u002F\u002F cleanJSONResponse removes the Markdown Formatting that LLMs often include\nfunc cleanJSONResponse(raw string) string {\n\tcleaned := strings.TrimSpace(raw)\n\t\u002F\u002F Remove Markdown Code Block\n\tif strings.HasPrefix(cleaned, \"```\") {\n\t\tlines := strings.Split(cleaned, \"\\n\")\n\t\tif len(lines) &gt;= 2 {\n\t\t\t\u002F\u002F Remove the first line (```json) and the last line (```)\n\t\t\tcleaned = strings.Join(lines[1:len(lines)-1], \"\\n\")\n\t\t}\n\t}\n\treturn strings.TrimSpace(cleaned)\n}\n\n\u002F\u002F validateSemantic checks additional business rules after a successful Unmarshal\nfunc (r *UserAnalysisResult) validateSemantic() error {\n\t\u002F\u002F 1. Check if Sentiment is within the acceptable range\n\tvalidSentiments := map[string]bool{\"positive\": true, \"negative\": true, \"neutral\": true}\n\tif !validSentiments[strings.ToLower(r.Sentiment)] {\n\t\treturn fmt.Errorf(\"invalid sentiment value: '%s'\", r.Sentiment)\n\t}\n\n\t\u002F\u002F 2. Check if Score is within the 0.0 - 1.0 range\n\tif r.Score &lt; 0.0 || r.Score &gt; 1.0 {\n\t\treturn fmt.Errorf(\"score out of bounds (0.0 - 1.0): %f\", r.Score)\n\t}\n\n\treturn nil\n}\n\n\u002F\u002F simulateLLMCall simulates an AI call, returning potentially problematic formats in early attempts\nfunc simulateLLMCall(attempt int) string {\n\tswitch attempt {\n\tcase 1:\n\t\t\u002F\u002F Attempt 1: Includes Markdown code block and malformed JSON (fails at Unmarshal)\n\t\treturn \"```json\\n{\\\"sentiment\\\": \\\"positive\\\", \\\"keywords\\\": [\\\"go\\\", \\\"ai\\\"], \\\"score\\\": 0.9\"\n\tcase 2:\n\t\t\u002F\u002F Attempt 2: Correct JSON, but invalid Semantic (Score out of bounds)\n\t\treturn \"{\\\"sentiment\\\": \\\"super_happy\\\", \\\"keywords\\\": [\\\"go\\\"], \\\"score\\\": 99.0}\"\n\tdefault:\n\t\t\u002F\u002F Attempt 3: Completely correct response\n\t\treturn \"{\\\"sentiment\\\": \\\"positive\\\", \\\"keywords\\\": [\\\"golang\\\", \\\"ai\\\"], \\\"score\\\": 0.95}\"\n\t}\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>Part 2: Main Safety Control and Execution Runner (main.go)\u003C\u002Fh3>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>\u002F\u002F SafeAIParseRunner is a function to call LLM + Parse + Validate with a Retry system\nfunc SafeAIParseRunner(ctx context.Context, maxRetries int) (*UserAnalysisResult, error) {\n\tvar lastErr error\n\n\tfor attempt := 1; attempt &lt;= maxRetries; attempt++ {\n\t\tfmt.Printf(\"🔄 [Attempt %d\u002F%d] Processing AI request...\\n\", attempt, maxRetries)\n\n\t\t\u002F\u002F 1. Call the LLM\n\t\trawOutput := simulateLLMCall(attempt)\n\n\t\t\u002F\u002F 2. Clean Up Strings\n\t\tcleanedOutput := cleanJSONResponse(rawOutput)\n\n\t\t\u002F\u002F 3. Structural Parsing Check\n\t\tvar result UserAnalysisResult\n\t\tif err := json.Unmarshal([]byte(cleanedOutput), &amp;result); err != nil {\n\t\t\tlastErr = fmt.Errorf(\"JSON Structural Error: %w\", err)\n\t\t\tfmt.Printf(\"❌ Failed: %v\\n\\n\", lastErr)\n\t\t\ttime.Sleep(200 * time.Millisecond)\n\t\t\tcontinue \u002F\u002F Skip to the next Retry attempt\n\t\t}\n\n\t\t\u002F\u002F 4. Semantic Validation Check\n\t\tif err := result.validateSemantic(); err != nil {\n\t\t\tlastErr = fmt.Errorf(\"Semantic Validation Error: %w\", err)\n\t\t\tfmt.Printf(\"❌ Failed: %v\\n\\n\", lastErr)\n\t\t\ttime.Sleep(200 * time.Millisecond)\n\t\t\tcontinue \u002F\u002F Skip to the next Retry attempt\n\t\t}\n\n\t\t\u002F\u002F If all checks pass\n\t\tfmt.Printf(\"✅ Success on attempt %d!\\n\", attempt)\n\t\treturn &amp;result, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"Exceeded maximum retries (%d times). Last Error: %w\", maxRetries, lastErr)\n}\n\nfunc main() {\n\tctx := context.Background()\n\tresult, err := SafeAIParseRunner(ctx, 3)\n\n\tif err != nil {\n\t\tfmt.Printf(\"🔴 System Error: %v\\n\", err)\n\t\t\u002F\u002F At this point, we can implement Fallback Behavior, e.g., returning a Default Struct\n\t\treturn\n\t}\n\n\tfmt.Println(\"\\n--- Safe output ready for further processing ---\")\n\tfmt.Printf(\"Sentiment : %s\\n\", result.Sentiment)\n\tfmt.Printf(\"Keywords  : %v\\n\", result.Keywords)\n\tfmt.Printf(\"Score     : %.2f\\n\", result.Score)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>Advanced Techniques: Structured Outputs &amp; Self-Correction Loop\u003C\u002Fh2>\u003Cp>Besides implementing Clean Up and Retry loops on the Go side, we should leverage API features to minimize errors at the source:\u003C\u002Fp>\u003Col>\u003Cli>\u003Cp>\u003Cstrong>JSON Schema Enforcement (Structured Outputs):\u003C\u002Fstrong> Define a \u003Cem>ResponseFormat\u003C\u002Fem> with a JSON Schema for the model (e.g., OpenAI Structured Outputs or Gemini Response Schema). This forces the model to reply exactly according to the structure, eliminating malformed JSON issues almost 100%.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>Self-Correction Retry (Feedback Loop):\u003C\u002Fstrong> When an error occurs on the first try, instead of sending the exact same prompt blindly, append the Go Error Message (e.g., \u003Cem>invalid sentiment value: super_happy\u003C\u002Fem>) to the end of the new prompt to tell the AI: \"You made a mistake here, please fix it.\" This significantly increases the success rate of the next attempt.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Fol>\u003Ch2>🎯 Daily Mission\u003C\u002Fh2>\u003Cp>Try running the code above on your machine and observe the execution and error handling during each attempt in the console.\u003C\u002Fp>\u003Cp>\u003Cstrong>Challenge:\u003C\u002Fstrong> If you encounter a case where the AI replies with a Refusal Text like \u003Cem>\"I cannot process questions regarding this policy,\"\u003C\u002Fem> which will definitely crash \u003Ccode>json.Unmarshal\u003C\u002Fcode>, how would you write a Custom Error Handler in Go to distinguish between \"Standard Malformed JSON\" and an \"AI Refusal\" so the system can handle it appropriately? Give it a try!\u003C\u002Fp>\u003Ch2>🙋‍♂️ FAQ (Frequently Asked Questions)\u003C\u002Fh2>\u003Ch3>Why not use Regex to extract just the JSON instead of checking for Markdown Code Blocks?\u003C\u002Fh3>\u003Cp>Using Regex to extract JSON is quite risky and consumes more resources. If the model returns deeply nested JSON or special characters, Regex might fail to match properly. Using simple string manipulation functions to strip out Markdown is much faster and safer in Go.\u003C\u002Fp>\u003Ch3>What should the maxRetries be set to?\u003C\u002Fh3>\u003Cp>Generally, 2 to 3 times is sufficient. Calling an LLM takes time (High Latency). If you retry too many times, the request might block the system and cause a timeout on the client side. It's recommended that once the retry quota is reached, you implement a Fallback system by returning default values to the user.\u003C\u002Fp>\u003Ch3>Do we need to write Semantic Validation for every single field?\u003C\u002Fh3>\u003Cp>Not necessarily. Focus on checking fields that heavily impact the core Business Logic, such as Status, Score, or fields that require further mathematical processing. General text fields (like descriptive notes) can be passed through to save processing time.\u003C\u002Fp>\u003Cdiv data-type=\"horizontalRule\">\u003Chr>\u003C\u002Fdiv>\u003Ch2>📝 Conclusion\u003C\u002Fh2>\u003Cp>When working with AI, we can never trust the output 100%. The techniques we learned today include Pre-validation, building a Clean Up function to strip Markdown from JSON, Semantic Validation to filter out garbage data, and using Retry Loops for automated immediate recovery. Setting up this 3-layer structure will make our Go Backend resilient against LLM unpredictability, allowing us to deploy to Production with confidence.\u003C\u002Fp>\u003Cp>\u003Cstrong>Coming up next (EP.169):\u003C\u002Fstrong> We've been intensely building AI Microservices in Go throughout this series, but many might be wondering, \"Why use Go for AI when Python is the industry favorite?\" In the next episode, we'll do a head-to-head comparison in \u003Cstrong>\"Benchmarking Go vs. Python for AI Pipelines — Analyzing Latency, Concurrency, and Memory Usage.\"\u003C\u002Fstrong> Don't miss it, Gophers!\u003C\u002Fp>\u003Cp>\u003Cstrong>Follow Superdev Academy on all platforms:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>🔵 Facebook: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">\u003Cstrong>Superdev Academy Thailand\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 YouTube: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\">\u003Cstrong>Superdev Academy Channel\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>📸 Instagram: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 TikTok: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🌐 Website: \u003C\u002Fstrong>\u003Ca rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fsuperdevacademy.com\">\u003Cstrong>superdevacademy.com\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003C\u002Fp>","56bytgond119_a59zgoyuve.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Fioxcjqq5glmfthm\u002F56bytgond119_a59zgoyuve.png","2026-08-04 06:09:16.208Z","76qprkevbgfdps8",{"keywords":15,"locale":51,"school_blog":61},[16,23,27,31,35,39,43,47],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-06-07 06:45:08.193Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":24,"updated_by":13},"2026-08-04 04:49:10.806Z","wkf0f2sfoww4cky","Go Backend",{"collectionId":17,"collectionName":18,"created":28,"created_by":13,"id":29,"name":30,"updated":28,"updated_by":13},"2026-08-04 05:28:58.694Z","vu2461mrhx1afzr","AI Error Handling",{"collectionId":17,"collectionName":18,"created":32,"created_by":13,"id":33,"name":34,"updated":32,"updated_by":13},"2026-06-11 16:14:34.250Z","01ajl5eq1joxocg","LLM",{"collectionId":17,"collectionName":18,"created":36,"created_by":13,"id":37,"name":38,"updated":36,"updated_by":13},"2026-08-04 05:29:28.009Z","l5rv57is5tqggzg","Retry Loop",{"collectionId":17,"collectionName":18,"created":40,"created_by":13,"id":41,"name":42,"updated":40,"updated_by":13},"2026-08-04 05:30:22.960Z","s7cvzvnp60of8ou","LLM Integration",{"collectionId":17,"collectionName":18,"created":44,"created_by":13,"id":45,"name":46,"updated":44,"updated_by":13},"2026-08-04 05:30:26.510Z","p2bj2l3jdw6vx08","Malformed JSON",{"collectionId":17,"collectionName":18,"created":48,"created_by":13,"id":49,"name":50,"updated":48,"updated_by":13},"2026-08-04 05:30:30.332Z","n2ffu6tz0tme9in","Safe Parsing",{"code":52,"collectionId":53,"collectionName":54,"created":55,"flag":56,"id":57,"is_default":58,"label":59,"updated":60},"en","pbc_1989393366","locales","2026-01-22 11:00:02.726Z","twemoji:flag-united-states","qv9c1llfov2d88z",false,"English","2026-04-10 15:42:46.825Z",{"category":62,"collectionId":63,"collectionName":64,"created":65,"expand":66,"id":81,"slug":82,"updated":83,"views":84},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs","2026-08-04 05:29:35.329Z",{"category":67},{"blogIds":68,"collectionId":69,"collectionName":70,"created":71,"created_by":13,"id":62,"image":72,"image_alt":73,"image_path":74,"label":75,"name":76,"priority":77,"publish_at":78,"scheduled_at":73,"status":79,"updated":80,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":76,"th":76},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-06-07 06:45:03.856Z","54u71ct4gosq2pv","golang-the-series-ep168-error-handling-in-ai","2026-08-11 12:54:04.937Z",115,"ioxcjqq5glmfthm",[20,25,29,33,37,41,45,49],"2026-08-11 04:41:17.368Z","Learn how to handle unexpected LLM responses in your Go backend. This guide covers practical techniques for cleaning up malformed JSON, implementing retry loops, and safe parsing to prevent system crashes.","Golang The Series EP.168: AI Error Handling — Dealing with Malformed JSON and Unexpected LLM Responses","2026-08-11 04:41:17.370Z","423vhnv3ckczcyn",{"th":82,"en":82}]