{"id":1065,"date":"2026-08-16T21:40:04","date_gmt":"2026-08-16T21:40:04","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1065"},"modified":"2026-08-16T21:44:48","modified_gmt":"2026-08-16T21:44:48","slug":"go-tutorials-go-error-management-easy-complete-tutorial","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/go-tutorials-go-error-management-easy-complete-tutorial\/","title":{"rendered":"Go Tutorials &#8211; Go Error Management \u2014 Easy, Complete Tutorial"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The most important rule to remember is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>In Go, errors are normal values. A function usually returns an <code>error<\/code>, and the caller decides what to do with it.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">The basic flow is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Call function] --&gt; B{Problem?}\n    B -- No --&gt; C&#91;Return result + nil]\n    B -- Yes --&gt; D&#91;Return zero value + error]\n    D --&gt; E{Caller checks err}\n    E -- err == nil --&gt; F&#91;Continue]\n    E -- err != nil --&gt; G&#91;Handle error]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>result, err := doSomething()\n\nif err != nil {\n    \/\/ handle the problem\n}\n\nfmt.Println(result)<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">1. Built-in \/ Standard Error Tools<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">First, one terminology correction that will make Go easier to understand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Go has:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Thing<\/th><th>Type<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>error<\/code><\/td><td>Built-in interface<\/td><td>Represents an error<\/td><\/tr><tr><td><code>errors<\/code><\/td><td>Standard library package<\/td><td>Creates\/checks errors<\/td><\/tr><tr><td><code>fmt<\/code><\/td><td>Standard library package<\/td><td>Creates formatted errors<\/td><\/tr><tr><td><code>panic<\/code><\/td><td>Built-in function<\/td><td>Stops normal execution<\/td><\/tr><tr><td><code>recover<\/code><\/td><td>Built-in function<\/td><td>Catches a panic<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>error\npanic()\nrecover()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">are built into the language.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import \"errors\"\nimport \"fmt\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">are <strong>packages from Go&#8217;s standard library<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">These are the main tools used for error management.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Because different situations need different tools:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Simple error                 \u2192 errors.New()\nError containing information \u2192 fmt.Errorf()\nCheck returned error         \u2192 if err != nil\nUnexpected fatal condition   \u2192 panic()\nCatch panic                  \u2192 recover()<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most application code will mainly use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if err != nil<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">along with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New(...)\nfmt.Errorf(...)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How \/ Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a complete example showing the main tools.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc divide(a, b int) (int, error) {\n\n\t\/\/ errors.New creates a simple error.\n\tif b == 0 {\n\t\treturn 0, errors.New(\"cannot divide by zero\")\n\t}\n\n\treturn a \/ b, nil\n}\n\nfunc main() {\n\n\tresult, err := divide(10, 0)\n\n\t\/\/ Always check the returned error.\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Result:\", result)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go run .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error: cannot divide by zero<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Remember<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>error       \u2192 type\/interface\nerrors      \u2192 package\nfmt         \u2192 package\npanic       \u2192 built-in function\nrecover     \u2192 built-in function<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. The <code>error<\/code> Interface<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is the <strong>most important concept<\/strong> behind Go errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>error<\/code> is a built-in interface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually, Go defines it like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>type error interface {\n\tError() string\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Anything having a method named <code>Error() string<\/code> can be an error.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Notice the capital <code>E<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error()<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It allows us to create our own custom error types.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use custom errors when the error should contain additional information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user ID\nfilename\nHTTP status\ndatabase information\nvalidation information<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a type and give it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error() string<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Usually inside the package responsible for that particular operation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport \"fmt\"\n\n\/\/ Create our own error type.\ntype AgeError struct {\n\tAge int\n}\n\n\/\/ Because AgeError has:\n\/\/\n\/\/ Error() string\n\/\/\n\/\/ it satisfies Go's built-in error interface.\nfunc (e AgeError) Error() string {\n\treturn fmt.Sprintf(\"age %d is too young\", e.Age)\n}\n\nfunc checkAge(age int) error {\n\n\tif age &lt; 18 {\n\t\treturn AgeError{Age: age}\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\n\terr := checkAge(15)\n\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Age accepted\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error: age 15 is too young<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important part is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func (e AgeError) Error() string<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because of this method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AgeError\n   \u2502\n   \u2502 has Error() string\n   \u25bc\nsatisfies\n   \u2502\n   \u25bc\nerror interface<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;AgeError struct] --&gt; B&#91;\"Error() string method\"]\n    B --&gt; C&#91;implements error interface]\n    C --&gt; D&#91;Can be returned as error]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You do <strong>not<\/strong> write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>implements error<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Go automatically recognizes it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Remember<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>type error interface {\n    Error() string\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">is the heart of Go error management.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. The <code>errors<\/code> Package<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Now we know that <code>error<\/code> is an interface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next question is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">How do I easily create an error?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">One common answer is the <code>errors<\/code> package.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import \"errors\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>errors<\/code> is a standard Go package for creating and working with errors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The simplest function is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New(\"user not found\")<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Because creating a custom struct for every simple error would be unnecessary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of creating:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>type MyError struct {\n\t...\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you can simply write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New(\"something went wrong\")<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use <code>errors.New()<\/code> when the error message is fixed\/simple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>invalid password\nuser not found\ndivision by zero\nfile is empty<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>err := errors.New(\"something went wrong\")<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Usually inside the function where the problem is detected.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc login(password string) error {\n\n\tif password != \"secret123\" {\n\n\t\t\/\/ Create and return a simple error.\n\t\treturn errors.New(\"invalid password\")\n\t}\n\n\t\/\/ nil means there was no error.\n\treturn nil\n}\n\nfunc main() {\n\n\terr := login(\"wrong-password\")\n\n\tif err != nil {\n\t\tfmt.Println(\"Login failed:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Login successful\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Login failed: invalid password<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The flow is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>login()\n   \u2502\n   \u251c\u2500\u2500 correct password\n   \u2502       \u2193\n   \u2502      nil\n   \u2502\n   \u2514\u2500\u2500 wrong password\n           \u2193\n      errors.New(...)\n           \u2193\n         error<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Most important pattern<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>return errors.New(\"something went wrong\")<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. <code>errors.New()<\/code> and <code>fmt.Errorf()<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You mentioned <strong>Error &amp; Errorf function<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The common functions you will actually use are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New()\nfmt.Errorf()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There isn&#8217;t a general built-in <code>Error()<\/code> function for creating errors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Error()<\/code> is normally the <strong>method required by the <code>error<\/code> interface<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><code>errors.New()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creates a simple fixed error.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New(\"user not found\")<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><code>fmt.Errorf()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creates an error containing dynamic information.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fmt.Errorf(\"user %s not found\", username)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Why <code>Errorf<\/code>?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose username is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>rajesh<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New(\"user not found\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you might want:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user rajesh not found<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s where:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fmt.Errorf()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">is useful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>errors.New() \u2192 simple\/static error\nfmt.Errorf() \u2192 dynamic\/formatted error<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc findUser(username string) error {\n\n\tif username == \"\" {\n\n\t\t\/\/ Fixed message:\n\t\t\/\/ errors.New is perfect here.\n\t\treturn errors.New(\"username cannot be empty\")\n\t}\n\n\tif username != \"rajesh\" {\n\n\t\t\/\/ Dynamic message:\n\t\t\/\/ username is inserted into the error.\n\t\treturn fmt.Errorf(\"user %s not found\", username)\n\t}\n\n\treturn nil\n}\n\nfunc main() {\n\n\terr := findUser(\"amit\")\n\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"User found\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error: user amit not found<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Easy rule<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Fixed error:\n\nerrors.New(\"database unavailable\")\n\n\nDynamic error:\n\nfmt.Errorf(\"user %s not found\", username)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>New       = simple message\nErrorf    = formatted message<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. Error Handling in Go<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Now we reach the part you will use constantly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Error handling means:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>call a function<\/li>\n\n\n\n<li>receive an error<\/li>\n\n\n\n<li>check the error<\/li>\n\n\n\n<li>decide what to do<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The famous Go pattern is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if err != nil {\n\t\/\/ handle error\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Go intentionally makes error handling explicit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can clearly see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>where an error happened\nwho handled it\nwhat happened after it<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Whenever a function returns:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>error<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">check it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>value, err := someFunction()\n\nif err != nil {\n\treturn\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Immediately after calling the function.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>result, err := divide(10, 2)\n\nif err != nil {\n\t...\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">rather than ignoring <code>err<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc divide(a, b float64) (float64, error) {\n\n\tif b == 0 {\n\t\treturn 0, errors.New(\"cannot divide by zero\")\n\t}\n\n\t\/\/ Success:\n\t\/\/ result + nil error\n\treturn a \/ b, nil\n}\n\nfunc main() {\n\n\tresult, err := divide(20, 4)\n\n\t\/\/ Step 1: check error.\n\tif err != nil {\n\t\tfmt.Println(\"Failed:\", err)\n\t\treturn\n\t}\n\n\t\/\/ Step 2: use result only after checking error.\n\tfmt.Println(\"Result:\", result)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Result: 5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now change:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>divide(20, 4)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>divide(20, 0)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Failed: cannot divide by zero<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Understand <code>(result, error)<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is extremely common:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func divide(a, b float64) (float64, error)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Success:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return a \/ b, nil<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Result = valid\nError  = nothing<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Failure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return 0, errors.New(\"cannot divide by zero\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Result = zero value\nError  = problem information<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mental model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;\"divide(20,4)\"] --&gt; B{Problem?}\n\n    B -- No --&gt; C&#91;\"return 5, nil\"]\n    B -- Yes --&gt; D&#91;\"return 0, error\"]\n\n    C --&gt; E&#91;\"err == nil\"]\n    D --&gt; F&#91;\"err != nil\"]\n\n    E --&gt; G&#91;Use result]\n    F --&gt; H&#91;Handle error]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">The #1 Go error pattern<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Memorize this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>result, err := someFunction()\n\nif err != nil {\n\tfmt.Println(err)\n\treturn\n}\n\nfmt.Println(result)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You will see this everywhere in Go.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. Errors vs Panics<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This distinction is extremely important.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An error means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Something went wrong, but the program can potentially deal with it.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>file doesn't exist\nwrong password\ndatabase unavailable\ninvalid user input\nnetwork timeout<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Return:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>error<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Panic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A panic means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Normal program execution cannot safely continue.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic(\"something seriously wrong\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A panic starts unwinding the current goroutine&#8217;s stack.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unless recovered, the program terminates and prints a stack trace.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When to use which?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Normal expected problem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return errors.New(\"user not found\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Exceptional\/programmer\/invariant problem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic(\"configuration must never be empty\")<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\n\/\/ Normal problem:\n\/\/ return an error.\nfunc checkUsername(username string) error {\n\n\tif username == \"\" {\n\t\treturn errors.New(\"username cannot be empty\")\n\t}\n\n\treturn nil\n}\n\n\/\/ Serious invariant problem:\n\/\/ panic.\nfunc startApplication(configLoaded bool) {\n\n\tif !configLoaded {\n\t\tpanic(\"application configuration was not loaded\")\n\t}\n\n\tfmt.Println(\"Application started\")\n}\n\nfunc main() {\n\n\t\/\/ NORMAL ERROR\n\terr := checkUsername(\"\")\n\n\tif err != nil {\n\t\tfmt.Println(\"Handled error:\", err)\n\t}\n\n\tfmt.Println(\"Program is still running\")\n\n\t\/\/ PANIC\n\tstartApplication(false)\n\n\t\/\/ This will never execute.\n\tfmt.Println(\"Finished\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Typical output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Handled error: username cannot be empty\nProgram is still running\npanic: application configuration was not loaded<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice the difference.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if err != nil {\n\tfmt.Println(err)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you control what happens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic(...)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">normal execution stops.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Easy comparison<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Error<\/th><th>Panic<\/th><\/tr><\/thead><tbody><tr><td>Normal problem<\/td><td>Exceptional situation<\/td><\/tr><tr><td>Returned<\/td><td>Thrown by <code>panic()<\/code><\/td><\/tr><tr><td>Caller handles it<\/td><td>Stops normal execution<\/td><\/tr><tr><td>Very common<\/td><td>Much less common<\/td><\/tr><tr><td><code>return err<\/code><\/td><td><code>panic(...)<\/code><\/td><\/tr><tr><td>Expected failures<\/td><td>Broken assumptions\/invariants<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Golden rule<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Don&#8217;t use panic just because something returned an error.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Usually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if err != nil {\n\treturn err\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">not:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if err != nil {\n\tpanic(err)\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. Converting Panic to Error<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes you are calling code that may panic, but you don&#8217;t want that panic to crash your program.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Go provides:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>recover()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But <code>recover()<\/code> works only from a deferred function during panic unwinding.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The pattern is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>defer func() {\n\tif r := recover(); r != nil {\n\t\t\/\/ panic was caught\n\t}\n}()<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>recover()<\/code> catches a panic.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It lets you turn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">into:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ordinary error<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use it mainly at controlled boundaries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server request handler\nlibrary wrapper\nplugin execution\nworker\/job boundary\ncode you don't control<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do <strong>not<\/strong> use <code>recover()<\/code> as normal error handling.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This looks strange initially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>defer func() {\n\tif r := recover(); r != nil {\n\t\terr = fmt.Errorf(\"panic occurred: %v\", r)\n\t}\n}()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>defer<\/code> waits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If a panic occurs, the deferred function runs and <code>recover()<\/code> catches the panic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport \"fmt\"\n\n\/\/ This function converts a panic into a normal error.\nfunc safeDivide(a, b int) (result int, err error) {\n\n\t\/\/ This deferred function runs when safeDivide finishes,\n\t\/\/ including when a panic happens.\n\tdefer func() {\n\n\t\t\/\/ recover() catches the panic.\n\t\tif r := recover(); r != nil {\n\n\t\t\t\/\/ Convert panic value into a normal error.\n\t\t\terr = fmt.Errorf(\"operation failed: %v\", r)\n\t\t}\n\t}()\n\n\tif b == 0 {\n\t\tpanic(\"cannot divide by zero\")\n\t}\n\n\tresult = a \/ b\n\treturn result, nil\n}\n\nfunc main() {\n\n\tresult, err := safeDivide(10, 0)\n\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Result:\", result)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error: operation failed: cannot divide by zero<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Without <code>recover()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic\n   \u2193\nprogram crashes<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With <code>recover()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic\n   \u2193\ndefer runs\n   \u2193\nrecover()\n   \u2193\nfmt.Errorf(...)\n   \u2193\nnormal error\n   \u2193\ncaller handles it<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Function starts] --&gt; B&#91;defer recovery function]\n    B --&gt; C&#91;Code runs]\n    C --&gt; D{Panic?}\n\n    D -- No --&gt; E&#91;Normal return]\n\n    D -- Yes --&gt; F&#91;Deferred function runs]\n    F --&gt; G&#91;\"recover()\"]\n    G --&gt; H&#91;\"Create error using fmt.Errorf\"]\n    H --&gt; I&#91;Return error to caller]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Important<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>recover()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">does <strong>not<\/strong> mean:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">catch every error.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">It only catches:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>panic()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Errors and panics are two different mechanisms.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The Complete Mental Model<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You can understand nearly all basic Go error handling with this picture:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Function executes] --&gt; B{Problem occurs?}\n\n    B -- No --&gt; C&#91;\"return result, nil\"]\n\n    B -- Yes --&gt; D{Normal \/ expected problem?}\n\n    D -- Yes --&gt; E&#91;\"return result, error\"]\n    E --&gt; F&#91;\"Caller checks err != nil\"]\n    F --&gt; G&#91;Handle \/ return \/ log error]\n\n    D -- No --&gt; H&#91;\"panic(...)\"]\n    H --&gt; I{recover exists?}\n\n    I -- No --&gt; J&#91;Program \/ goroutine terminates]\n    I -- Yes --&gt; K&#91;\"recover()\"]\n    K --&gt; L&#91;Possibly convert panic to error]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final Comparison \u2014 Everything Side by Side<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Concept<\/th><th>What is it?<\/th><th>Main purpose<\/th><th>Typical syntax<\/th><th>When to use<\/th><th>Normal?<\/th><\/tr><\/thead><tbody><tr><td><code>error<\/code><\/td><td>Built-in interface<\/td><td>Represent a problem<\/td><td><code>var err error<\/code><\/td><td>Nearly all recoverable failures<\/td><td>Yes<\/td><\/tr><tr><td><code>Error() string<\/code><\/td><td>Interface method<\/td><td>Makes a type satisfy <code>error<\/code><\/td><td><code>func (e MyError) Error() string<\/code><\/td><td>Custom errors<\/td><td>Yes<\/td><\/tr><tr><td><code>errors<\/code><\/td><td>Standard package<\/td><td>Error utilities<\/td><td><code>import \"errors\"<\/code><\/td><td>Creating\/checking errors<\/td><td>Yes<\/td><\/tr><tr><td><code>errors.New()<\/code><\/td><td>Function<\/td><td>Create simple error<\/td><td><code>errors.New(\"invalid input\")<\/code><\/td><td>Static error message<\/td><td>Yes<\/td><\/tr><tr><td><code>fmt.Errorf()<\/code><\/td><td>Function<\/td><td>Create formatted error<\/td><td><code>fmt.Errorf(\"user %s not found\", name)<\/code><\/td><td>Dynamic error message<\/td><td>Yes<\/td><\/tr><tr><td><code>if err != nil<\/code><\/td><td>Error-handling pattern<\/td><td>Detect returned errors<\/td><td><code>if err != nil {...}<\/code><\/td><td>After error-returning function calls<\/td><td>Extremely common<\/td><\/tr><tr><td><code>nil<\/code><\/td><td>No error<\/td><td>Indicates success<\/td><td><code>return value, nil<\/code><\/td><td>Successful operation<\/td><td>Extremely common<\/td><\/tr><tr><td><code>panic()<\/code><\/td><td>Built-in function<\/td><td>Stop normal execution<\/td><td><code>panic(\"bad state\")<\/code><\/td><td>Exceptional\/unrecoverable situation<\/td><td>Rare<\/td><\/tr><tr><td><code>recover()<\/code><\/td><td>Built-in function<\/td><td>Catch panic<\/td><td><code>recover()<\/code><\/td><td>Controlled panic boundary<\/td><td>Rare<\/td><\/tr><tr><td><code>defer<\/code> + <code>recover<\/code><\/td><td>Recovery pattern<\/td><td>Convert\/catch panic<\/td><td><code>defer func(){...}()<\/code><\/td><td>Server\/library boundaries<\/td><td>Rare<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>errors.New<\/code> vs <code>fmt.Errorf<\/code> vs <code>panic<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is probably the most useful decision table to remember:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Situation<\/th><th>Use<\/th><\/tr><\/thead><tbody><tr><td>Simple fixed error<\/td><td><code>errors.New()<\/code><\/td><\/tr><tr><td>Error needs variables\/data<\/td><td><code>fmt.Errorf()<\/code><\/td><\/tr><tr><td>Function succeeds<\/td><td><code>nil<\/code><\/td><\/tr><tr><td>Function returns error<\/td><td><code>if err != nil<\/code><\/td><\/tr><tr><td>Normal expected failure<\/td><td><code>error<\/code><\/td><\/tr><tr><td>Truly exceptional impossible state<\/td><td><code>panic()<\/code><\/td><\/tr><tr><td>Need to catch a panic<\/td><td><code>recover()<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Simple\nreturn errors.New(\"invalid password\")<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Dynamic\nreturn fmt.Errorf(\"user %s not found\", username)<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Success\nreturn result, nil<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Handle\nif err != nil {\n\treturn\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Exceptional\npanic(\"database configuration missing\")<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Catch panic\nif r := recover(); r != nil {\n\tfmt.Println(r)\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">One Very Important Go Pattern to Memorize<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">If you remember only <strong>one piece of code<\/strong> from this tutorial, remember this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nfunc divide(a, b int) (int, error) {\n\n\tif b == 0 {\n\t\treturn 0, errors.New(\"cannot divide by zero\")\n\t}\n\n\treturn a \/ b, nil\n}\n\nfunc main() {\n\n\tresult, err := divide(10, 2)\n\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(\"Result:\", result)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The entire philosophy is essentially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Function\n   \u2502\n   \u251c\u2500\u2500 Success\n   \u2502      \u2193\n   \u2502  result, nil\n   \u2502\n   \u2514\u2500\u2500 Failure\n          \u2193\n      zero, error\n          \u2502\n          \u25bc\n    Caller checks\n          \u2502\n     err != nil<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Final Memory Trick<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>error           = WHAT represents a problem\n\nerrors.New()    = CREATE a simple problem\n\nfmt.Errorf()    = CREATE a problem with values\/details\n\nerr != nil      = CHECK whether a problem happened\n\npanic()         = STOP because something exceptional happened\n\nrecover()       = CATCH a panic\n\ndefer           = WHERE recover is normally placed<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And the single most important distinction:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Errors are normal in Go. Panics are exceptional.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">For most Go programs, you will spend <strong>far more time writing <code>if err != nil<\/code> than writing <code>panic()<\/code> or <code>recover()<\/code>.<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The most important rule to remember is: In Go, errors are normal values. A function usually returns an error, and the caller decides what to do with&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1065","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1065","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1065"}],"version-history":[{"count":2,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1065\/revisions"}],"predecessor-version":[{"id":1070,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1065\/revisions\/1070"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1065"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1065"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1065"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}