[{"data":1,"prerenderedAt":3407},["ShallowReactive",2],{"navigation_docs_en":3,"-en-concepts-error-model":342,"-en-concepts-error-model-surround":3402},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":78,"body":344,"description":3395,"extension":3396,"links":3397,"meta":3398,"navigation":3399,"path":79,"seo":3400,"stem":80,"__hash__":3401},"docs_en\u002Fen\u002F2.concepts\u002F3.error-model.md",{"type":345,"value":346,"toc":3379},"minimark",[347,369,585,603,618,623,649,667,671,692,734,909,928,949,954,957,1058,1084,1088,1091,1211,1215,1261,1265,1276,1349,1353,1364,1557,1574,1577,1581,1604,1725,1855,1858,2114,2264,2267,2413,2416,2425,2542,2555,2571,2575,2586,2690,2715,2743,2747,2764,3241,3249,3260,3288,3291,3310,3314,3341,3345,3375],[348,349,350,351,355,356,363,364,368],"p",{},"The gpsystem error model has two layers and a single principle: ",[352,353,354],"strong",{},"one error shape on the wire, one mapping in the code."," Every error a gpsystem service returns (a validation failure, a 404, a panic, a database timeout) reaches the client as an ",[357,358,362],"a",{"href":359,"rel":360},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9457",[361],"nofollow","RFC 9457"," problem document with ",[365,366,367],"code",{},"Content-Type: application\u002Fproblem+json",":",[370,371,376],"pre",{"className":372,"code":373,"language":374,"meta":375,"style":375},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"about:blank\",\n  \"title\": \"Unprocessable Entity\",\n  \"status\": 422,\n  \"detail\": \"One or more fields failed validation.\",\n  \"instance\": \"\u002Fapi\u002Fv1\u002Fshop\u002Forders\",\n  \"errors\": [\n    { \"field\": \"quantity\", \"rule\": \"min\", \"message\": \"must be at least 1\" }\n  ]\n}\n","json","",[365,377,378,387,414,435,453,474,495,510,573,579],{"__ignoreMap":375},[379,380,383],"span",{"class":381,"line":382},"line",1,[379,384,386],{"class":385},"sMK4o","{\n",[379,388,390,393,397,400,402,405,409,411],{"class":381,"line":389},2,[379,391,392],{"class":385},"  \"",[379,394,396],{"class":395},"spNyl","type",[379,398,399],{"class":385},"\"",[379,401,368],{"class":385},[379,403,404],{"class":385}," \"",[379,406,408],{"class":407},"sfazB","about:blank",[379,410,399],{"class":385},[379,412,413],{"class":385},",\n",[379,415,417,419,422,424,426,428,431,433],{"class":381,"line":416},3,[379,418,392],{"class":385},[379,420,421],{"class":395},"title",[379,423,399],{"class":385},[379,425,368],{"class":385},[379,427,404],{"class":385},[379,429,430],{"class":407},"Unprocessable Entity",[379,432,399],{"class":385},[379,434,413],{"class":385},[379,436,438,440,443,445,447,451],{"class":381,"line":437},4,[379,439,392],{"class":385},[379,441,442],{"class":395},"status",[379,444,399],{"class":385},[379,446,368],{"class":385},[379,448,450],{"class":449},"sbssI"," 422",[379,452,413],{"class":385},[379,454,456,458,461,463,465,467,470,472],{"class":381,"line":455},5,[379,457,392],{"class":385},[379,459,460],{"class":395},"detail",[379,462,399],{"class":385},[379,464,368],{"class":385},[379,466,404],{"class":385},[379,468,469],{"class":407},"One or more fields failed validation.",[379,471,399],{"class":385},[379,473,413],{"class":385},[379,475,477,479,482,484,486,488,491,493],{"class":381,"line":476},6,[379,478,392],{"class":385},[379,480,481],{"class":395},"instance",[379,483,399],{"class":385},[379,485,368],{"class":385},[379,487,404],{"class":385},[379,489,490],{"class":407},"\u002Fapi\u002Fv1\u002Fshop\u002Forders",[379,492,399],{"class":385},[379,494,413],{"class":385},[379,496,498,500,503,505,507],{"class":381,"line":497},7,[379,499,392],{"class":385},[379,501,502],{"class":395},"errors",[379,504,399],{"class":385},[379,506,368],{"class":385},[379,508,509],{"class":385}," [\n",[379,511,513,516,518,522,524,526,528,531,533,536,538,541,543,545,547,550,552,554,556,559,561,563,565,568,570],{"class":381,"line":512},8,[379,514,515],{"class":385},"    {",[379,517,404],{"class":385},[379,519,521],{"class":520},"sBMFI","field",[379,523,399],{"class":385},[379,525,368],{"class":385},[379,527,404],{"class":385},[379,529,530],{"class":407},"quantity",[379,532,399],{"class":385},[379,534,535],{"class":385},",",[379,537,404],{"class":385},[379,539,540],{"class":520},"rule",[379,542,399],{"class":385},[379,544,368],{"class":385},[379,546,404],{"class":385},[379,548,549],{"class":407},"min",[379,551,399],{"class":385},[379,553,535],{"class":385},[379,555,404],{"class":385},[379,557,558],{"class":520},"message",[379,560,399],{"class":385},[379,562,368],{"class":385},[379,564,404],{"class":385},[379,566,567],{"class":407},"must be at least 1",[379,569,399],{"class":385},[379,571,572],{"class":385}," }\n",[379,574,576],{"class":381,"line":575},9,[379,577,578],{"class":385},"  ]\n",[379,580,582],{"class":381,"line":581},10,[379,583,584],{"class":385},"}\n",[348,586,587,588,593,594,599,600,602],{},"The wire-facing shape is ",[357,589,590],{"href":119},[365,591,592],{},"httperr.Problem","; underneath, services and repositories return conventional Go errors. When built with the ",[352,595,596],{},[365,597,598],{},"errs"," package, those carry a stack trace, a machine-readable code and a client-safe public message. This page describes the whole chain: the ",[365,601,598],{}," API, and how a repository error becomes an HTTP response, a log record and a Sentry issue.",[348,604,605,606,609,610,613,614,617],{},"The duality is expressed with attributes: ",[365,607,608],{},"Public"," and ",[365,611,612],{},"Status"," are the render side (what the client sees), ",[365,615,616],{},"Code",", the wrap chain and the stack are the report side (what goes to the log\u002FSentry). Rendering is done not by the individual error but by a single central mapping.",[619,620,622],"h2",{"id":621},"why-two-messages","Why two messages",[348,624,625,626,629,630,633,634,637,638,641,642,645,646,648],{},"An error has two audiences. The internal message (",[365,627,628],{},"Error()",") is for you: full detail, wrapped causes, the failing query. The ",[352,631,632],{},"public"," message is the only text a client may see. Separating the two means a stray ",[365,635,636],{},"err.Error()"," in a response can never leak internals: the ",[365,639,640],{},"httperr"," handler reads ",[365,643,644],{},"PublicOf",", never ",[365,647,628],{},".",[348,650,651,652,655,656,659,660,609,663,666],{},"The stack trace and the wrap chain go to the log on every 5xx. They reach an HTTP response ",[352,653,654],{},"only"," in dev mode (",[365,657,658],{},"HTTP_EXPOSE_INTERNAL_ERRORS=true","), as the Problem's ",[365,661,662],{},"stack",[365,664,665],{},"chain"," extension members; in production they live exclusively in the log.",[619,668,670],{"id":669},"the-errs-api","The errs API",[370,672,676],{"className":673,"code":674,"language":675,"meta":375,"style":375},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fgpsystem\u002Ferrs\"\n","go",[365,677,678],{"__ignoreMap":375},[379,679,680,684,686,689],{"class":381,"line":382},[379,681,683],{"class":682},"s7zQu","import",[379,685,404],{"class":385},[379,687,688],{"class":520},"github.com\u002Fgp-system\u002Fgpsystem\u002Ferrs",[379,690,691],{"class":385},"\"\n",[348,693,694,696,697,700,701,704,705,708,709,711,712,715,716,718,719,722,723,726,727,726,730,733],{},[365,695,598],{}," adds three things to a plain ",[365,698,699],{},"fmt.Errorf"," chain: ",[352,702,703],{},"the location of every wrap"," plus a full ",[352,706,707],{},"stack trace"," from the point of origin, a machine-readable ",[352,710,365],{},", and a ",[352,713,714],{},"client-safe public message",". ",[365,717,628],{}," output stays byte-identical to the ",[365,720,721],{},"\"pkg: op: %w\""," convention, and ",[365,724,725],{},"errors.Is"," \u002F ",[365,728,729],{},"errors.As",[365,731,732],{},"errors.Join"," work unchanged. It can be adopted gradually, boundary by boundary.",[370,735,737],{"className":673,"code":736,"language":675,"meta":375,"style":375},"func New(msg string, attrs ...Attr) error\nfunc Errorf(format string, args ...any) error            \u002F\u002F %w works (single and multi)\nfunc Wrap(err error, msg string, attrs ...Attr) error    \u002F\u002F returns nil when err is nil\nfunc Wrapf(err error, format string, args ...any) error  \u002F\u002F returns nil when err is nil\nfunc NewPanic(rec any, attrs ...Attr) error              \u002F\u002F from a recovered panic; the stack points at the panic site\n",[365,738,739,775,808,844,879],{"__ignoreMap":375},[379,740,741,744,748,751,755,758,760,763,766,769,772],{"class":381,"line":382},[379,742,743],{"class":385},"func",[379,745,747],{"class":746},"s2Zo4"," New",[379,749,750],{"class":385},"(",[379,752,754],{"class":753},"sHdIc","msg",[379,756,757],{"class":395}," string",[379,759,535],{"class":385},[379,761,762],{"class":753}," attrs",[379,764,765],{"class":385}," ...",[379,767,768],{"class":520},"Attr",[379,770,771],{"class":385},")",[379,773,774],{"class":395}," error\n",[379,776,777,779,782,784,787,789,791,794,796,799,801,804],{"class":381,"line":389},[379,778,743],{"class":385},[379,780,781],{"class":746}," Errorf",[379,783,750],{"class":385},[379,785,786],{"class":753},"format",[379,788,757],{"class":395},[379,790,535],{"class":385},[379,792,793],{"class":753}," args",[379,795,765],{"class":385},[379,797,798],{"class":520},"any",[379,800,771],{"class":385},[379,802,803],{"class":395}," error",[379,805,807],{"class":806},"sHwdD","            \u002F\u002F %w works (single and multi)\n",[379,809,810,812,815,817,820,822,824,827,829,831,833,835,837,839,841],{"class":381,"line":416},[379,811,743],{"class":385},[379,813,814],{"class":746}," Wrap",[379,816,750],{"class":385},[379,818,819],{"class":753},"err",[379,821,803],{"class":395},[379,823,535],{"class":385},[379,825,826],{"class":753}," msg",[379,828,757],{"class":395},[379,830,535],{"class":385},[379,832,762],{"class":753},[379,834,765],{"class":385},[379,836,768],{"class":520},[379,838,771],{"class":385},[379,840,803],{"class":395},[379,842,843],{"class":806},"    \u002F\u002F returns nil when err is nil\n",[379,845,846,848,851,853,855,857,859,862,864,866,868,870,872,874,876],{"class":381,"line":437},[379,847,743],{"class":385},[379,849,850],{"class":746}," Wrapf",[379,852,750],{"class":385},[379,854,819],{"class":753},[379,856,803],{"class":395},[379,858,535],{"class":385},[379,860,861],{"class":753}," format",[379,863,757],{"class":395},[379,865,535],{"class":385},[379,867,793],{"class":753},[379,869,765],{"class":385},[379,871,798],{"class":520},[379,873,771],{"class":385},[379,875,803],{"class":395},[379,877,878],{"class":806},"  \u002F\u002F returns nil when err is nil\n",[379,880,881,883,886,888,891,894,896,898,900,902,904,906],{"class":381,"line":455},[379,882,743],{"class":385},[379,884,885],{"class":746}," NewPanic",[379,887,750],{"class":385},[379,889,890],{"class":753},"rec",[379,892,893],{"class":520}," any",[379,895,535],{"class":385},[379,897,762],{"class":753},[379,899,765],{"class":385},[379,901,768],{"class":520},[379,903,771],{"class":385},[379,905,803],{"class":395},[379,907,908],{"class":806},"              \u002F\u002F from a recovered panic; the stack points at the panic site\n",[348,910,911,609,914,917,918,921,922,924,925,648],{},[365,912,913],{},"Wrap",[365,915,916],{},"Wrapf"," return ",[365,919,920],{},"nil"," for a ",[365,923,920],{}," input, so they are safe in one-line returns: ",[365,926,927],{},"return errs.Wrap(tx.Commit(ctx), \"pg: commit\")",[348,929,930,933,934,937,938,940,941,944,945,948],{},[365,931,932],{},"NewPanic"," builds an error from a recovered panic value, capturing the stack at the recovery point, which, thanks to the unwinding, still contains the panic site. An ",[365,935,936],{},"error","-typed panic value becomes the cause (",[365,939,725],{},"\u002F",[365,942,943],{},"As"," work on it); any other value is formatted as ",[365,946,947],{},"panic: %v",". The kit's Fiber and chi recoverers call it for you, so a panic is logged with the same structured chain and stack as any other error.",[950,951,953],"h3",{"id":952},"attributes","Attributes",[348,955,956],{},"Attached at construction time:",[370,958,960],{"className":673,"code":959,"language":675,"meta":375,"style":375},"errs.Code(\"shop_api_order_insert_failed\") \u002F\u002F machine-readable, stable across releases\nerrs.Public(\"Saving the order failed.\")   \u002F\u002F the only text a client may see\nerrs.Status(http.StatusConflict)          \u002F\u002F HTTP status httperr maps this error to (default 500)\nerrs.With(\"order_id\", id)                 \u002F\u002F allowlisted metadata: pass scalars, not whole structs\n",[365,961,962,985,1007,1030],{"__ignoreMap":375},[379,963,964,967,969,971,973,975,978,980,982],{"class":381,"line":382},[379,965,598],{"class":966},"sTEyZ",[379,968,648],{"class":385},[379,970,616],{"class":746},[379,972,750],{"class":385},[379,974,399],{"class":385},[379,976,977],{"class":407},"shop_api_order_insert_failed",[379,979,399],{"class":385},[379,981,771],{"class":385},[379,983,984],{"class":806}," \u002F\u002F machine-readable, stable across releases\n",[379,986,987,989,991,993,995,997,1000,1002,1004],{"class":381,"line":389},[379,988,598],{"class":966},[379,990,648],{"class":385},[379,992,608],{"class":746},[379,994,750],{"class":385},[379,996,399],{"class":385},[379,998,999],{"class":407},"Saving the order failed.",[379,1001,399],{"class":385},[379,1003,771],{"class":385},[379,1005,1006],{"class":806},"   \u002F\u002F the only text a client may see\n",[379,1008,1009,1011,1013,1015,1017,1020,1022,1025,1027],{"class":381,"line":416},[379,1010,598],{"class":966},[379,1012,648],{"class":385},[379,1014,612],{"class":746},[379,1016,750],{"class":385},[379,1018,1019],{"class":966},"http",[379,1021,648],{"class":385},[379,1023,1024],{"class":966},"StatusConflict",[379,1026,771],{"class":385},[379,1028,1029],{"class":806},"          \u002F\u002F HTTP status httperr maps this error to (default 500)\n",[379,1031,1032,1034,1036,1039,1041,1043,1046,1048,1050,1053,1055],{"class":381,"line":437},[379,1033,598],{"class":966},[379,1035,648],{"class":385},[379,1037,1038],{"class":746},"With",[379,1040,750],{"class":385},[379,1042,399],{"class":385},[379,1044,1045],{"class":407},"order_id",[379,1047,399],{"class":385},[379,1049,535],{"class":385},[379,1051,1052],{"class":966}," id",[379,1054,771],{"class":385},[379,1056,1057],{"class":806},"                 \u002F\u002F allowlisted metadata: pass scalars, not whole structs\n",[348,1059,1060,1062,1063,1065,1066,1068,1069,1072,1073,1075,1076,1079,1080,1083],{},[365,1061,616],{}," identifies the failure mode in the log, in Sentry (the issue title and grouping fingerprint, see ",[357,1064,237],{"href":238},") and in the RFC 9457 response's ",[365,1067,365],{}," member. Pick a stable snake_case name: ",[365,1070,1071],{},"\u003Cmodule>_\u003Caction>_failed"," for unexpected operational failures (e.g. ",[365,1074,977],{},"), ",[365,1077,1078],{},"\u003Cmodule>_\u003Ccondition>"," for expected business errors (e.g. ",[365,1081,1082],{},"shop_out_of_stock",").",[950,1085,1087],{"id":1086},"accessors","Accessors",[348,1089,1090],{},"They walk the chain from the outside in, the outermost set value wins:",[370,1092,1094],{"className":673,"code":1093,"language":675,"meta":375,"style":375},"errs.CodeOf(err) string       \u002F\u002F the first code in the chain, or \"\"\nerrs.PublicOf(err) string     \u002F\u002F the first public message in the chain, or \"\"\nerrs.StatusOf(err) int        \u002F\u002F the first HTTP status in the chain, or 0 (httperr falls back to 500)\nerrs.Chain(err) []errs.Step   \u002F\u002F per-level message, wrap site and attrs, outermost first\nerrs.Frames(err) []errs.Frame \u002F\u002F full stack from the point of origin\n",[365,1095,1096,1116,1135,1156,1184],{"__ignoreMap":375},[379,1097,1098,1100,1102,1105,1107,1109,1111,1113],{"class":381,"line":382},[379,1099,598],{"class":966},[379,1101,648],{"class":385},[379,1103,1104],{"class":746},"CodeOf",[379,1106,750],{"class":385},[379,1108,819],{"class":966},[379,1110,771],{"class":385},[379,1112,757],{"class":395},[379,1114,1115],{"class":806},"       \u002F\u002F the first code in the chain, or \"\"\n",[379,1117,1118,1120,1122,1124,1126,1128,1130,1132],{"class":381,"line":389},[379,1119,598],{"class":966},[379,1121,648],{"class":385},[379,1123,644],{"class":746},[379,1125,750],{"class":385},[379,1127,819],{"class":966},[379,1129,771],{"class":385},[379,1131,757],{"class":395},[379,1133,1134],{"class":806},"     \u002F\u002F the first public message in the chain, or \"\"\n",[379,1136,1137,1139,1141,1144,1146,1148,1150,1153],{"class":381,"line":416},[379,1138,598],{"class":966},[379,1140,648],{"class":385},[379,1142,1143],{"class":746},"StatusOf",[379,1145,750],{"class":385},[379,1147,819],{"class":966},[379,1149,771],{"class":385},[379,1151,1152],{"class":395}," int",[379,1154,1155],{"class":806},"        \u002F\u002F the first HTTP status in the chain, or 0 (httperr falls back to 500)\n",[379,1157,1158,1160,1162,1165,1167,1169,1171,1174,1176,1178,1181],{"class":381,"line":437},[379,1159,598],{"class":966},[379,1161,648],{"class":385},[379,1163,1164],{"class":746},"Chain",[379,1166,750],{"class":385},[379,1168,819],{"class":966},[379,1170,771],{"class":385},[379,1172,1173],{"class":385}," []",[379,1175,598],{"class":966},[379,1177,648],{"class":385},[379,1179,1180],{"class":966},"Step   ",[379,1182,1183],{"class":806},"\u002F\u002F per-level message, wrap site and attrs, outermost first\n",[379,1185,1186,1188,1190,1193,1195,1197,1199,1201,1203,1205,1208],{"class":381,"line":455},[379,1187,598],{"class":966},[379,1189,648],{"class":385},[379,1191,1192],{"class":746},"Frames",[379,1194,750],{"class":385},[379,1196,819],{"class":966},[379,1198,771],{"class":385},[379,1200,1173],{"class":385},[379,1202,598],{"class":966},[379,1204,648],{"class":385},[379,1206,1207],{"class":966},"Frame ",[379,1209,1210],{"class":806},"\u002F\u002F full stack from the point of origin\n",[950,1212,1214],{"id":1213},"stack-semantics","Stack semantics",[1216,1217,1218,1234,1240,1249,1255],"ul",{},[1219,1220,1221,1224,1225,1227,1228,1230,1231,1233],"li",{},[352,1222,1223],{},"Capture once."," The full stack is captured only at the deepest ",[365,1226,598],{}," error in a chain. Wrapping an error that already carries a stack records the new wrap site (visible in ",[365,1229,1164],{},") but does not re-capture the stack: ",[365,1232,1192],{}," always points at the point of origin.",[1219,1235,1236,1239],{},[352,1237,1238],{},"Depth."," The stack is bounded at 32 frames: the kit's request paths are ~15–20 frames deep, so this covers them while keeping log records small.",[1219,1241,1242,1245,1246,1248],{},[352,1243,1244],{},"Joined errors."," With ",[365,1247,732],{},", if any branch already carries a stack, no new one is captured: one stack per chain keeps logs lean. The other branch's origin may be lost; a deliberate trade-off.",[1219,1250,1251,1254],{},[352,1252,1253],{},"Goroutines."," The stack is captured where the error is constructed: an error created in a goroutine carries the goroutine's frames, not the parent's.",[1219,1256,1257,1260],{},[352,1258,1259],{},"Lazy resolution."," Program counters resolve to file\u002Fline\u002Ffunction only when the error is actually logged.",[950,1262,1264],{"id":1263},"sentinels-stay-errorsnew","Sentinels stay errors.New",[348,1266,1267,1268,1271,1272,1275],{},"A package-level ",[365,1269,1270],{},"errs.New"," sentinel would freeze an init-time stack and share mutable attrs across requests. Define a bare sentinel with stdlib ",[365,1273,1274],{},"errors.New"," and wrap it per call:",[370,1277,1279],{"className":673,"code":1278,"language":675,"meta":375,"style":375},"var ErrNotFound = errors.New(\"shop: product not found\")\n\nreturn errs.Wrap(ErrNotFound, \"product: get\") \u002F\u002F errors.Is(…, ErrNotFound) still holds\n",[365,1280,1281,1312,1318],{"__ignoreMap":375},[379,1282,1283,1286,1289,1292,1295,1297,1300,1302,1304,1307,1309],{"class":381,"line":382},[379,1284,1285],{"class":385},"var",[379,1287,1288],{"class":966}," ErrNotFound ",[379,1290,1291],{"class":385},"=",[379,1293,1294],{"class":966}," errors",[379,1296,648],{"class":385},[379,1298,1299],{"class":746},"New",[379,1301,750],{"class":385},[379,1303,399],{"class":385},[379,1305,1306],{"class":407},"shop: product not found",[379,1308,399],{"class":385},[379,1310,1311],{"class":385},")\n",[379,1313,1314],{"class":381,"line":389},[379,1315,1317],{"emptyLinePlaceholder":1316},true,"\n",[379,1319,1320,1323,1326,1328,1330,1332,1335,1337,1339,1342,1344,1346],{"class":381,"line":416},[379,1321,1322],{"class":682},"return",[379,1324,1325],{"class":966}," errs",[379,1327,648],{"class":385},[379,1329,913],{"class":746},[379,1331,750],{"class":385},[379,1333,1334],{"class":966},"ErrNotFound",[379,1336,535],{"class":385},[379,1338,404],{"class":385},[379,1340,1341],{"class":407},"product: get",[379,1343,399],{"class":385},[379,1345,771],{"class":385},[379,1347,1348],{"class":806}," \u002F\u002F errors.Is(…, ErrNotFound) still holds\n",[619,1350,1352],{"id":1351},"errsdefine-named-failure-modes","errs.Define: named failure modes",[348,1354,1355,1356,1359,1360,1363],{},"The common case, though, is a sentinel that should also carry ",[352,1357,1358],{},"a code, a public message or a status",". That is what a ",[365,1361,1362],{},"Definition"," is for: it is immutable and stackless, so it is safe as a package-level var, while every occurrence still gets its own call-site stack:",[370,1365,1367],{"className":673,"code":1366,"language":675,"meta":375,"style":375},"func Define(code string, attrs ...Attr) *Definition\n\nfunc (d *Definition) New(msg string, attrs ...Attr) error\nfunc (d *Definition) Newf(format string, args ...any) error\nfunc (d *Definition) Wrap(err error, msg string, attrs ...Attr) error   \u002F\u002F nil-safe\nfunc (d *Definition) Wrapf(err error, format string, args ...any) error\n",[365,1368,1369,1398,1402,1439,1474,1517],{"__ignoreMap":375},[379,1370,1371,1373,1376,1378,1380,1382,1384,1386,1388,1390,1392,1395],{"class":381,"line":382},[379,1372,743],{"class":385},[379,1374,1375],{"class":746}," Define",[379,1377,750],{"class":385},[379,1379,365],{"class":753},[379,1381,757],{"class":395},[379,1383,535],{"class":385},[379,1385,762],{"class":753},[379,1387,765],{"class":385},[379,1389,768],{"class":520},[379,1391,771],{"class":385},[379,1393,1394],{"class":385}," *",[379,1396,1397],{"class":520},"Definition\n",[379,1399,1400],{"class":381,"line":389},[379,1401,1317],{"emptyLinePlaceholder":1316},[379,1403,1404,1406,1409,1412,1415,1417,1419,1421,1423,1425,1427,1429,1431,1433,1435,1437],{"class":381,"line":416},[379,1405,743],{"class":385},[379,1407,1408],{"class":385}," (",[379,1410,1411],{"class":753},"d ",[379,1413,1414],{"class":385},"*",[379,1416,1362],{"class":520},[379,1418,771],{"class":385},[379,1420,747],{"class":746},[379,1422,750],{"class":385},[379,1424,754],{"class":753},[379,1426,757],{"class":395},[379,1428,535],{"class":385},[379,1430,762],{"class":753},[379,1432,765],{"class":385},[379,1434,768],{"class":520},[379,1436,771],{"class":385},[379,1438,774],{"class":395},[379,1440,1441,1443,1445,1447,1449,1451,1453,1456,1458,1460,1462,1464,1466,1468,1470,1472],{"class":381,"line":437},[379,1442,743],{"class":385},[379,1444,1408],{"class":385},[379,1446,1411],{"class":753},[379,1448,1414],{"class":385},[379,1450,1362],{"class":520},[379,1452,771],{"class":385},[379,1454,1455],{"class":746}," Newf",[379,1457,750],{"class":385},[379,1459,786],{"class":753},[379,1461,757],{"class":395},[379,1463,535],{"class":385},[379,1465,793],{"class":753},[379,1467,765],{"class":385},[379,1469,798],{"class":520},[379,1471,771],{"class":385},[379,1473,774],{"class":395},[379,1475,1476,1478,1480,1482,1484,1486,1488,1490,1492,1494,1496,1498,1500,1502,1504,1506,1508,1510,1512,1514],{"class":381,"line":455},[379,1477,743],{"class":385},[379,1479,1408],{"class":385},[379,1481,1411],{"class":753},[379,1483,1414],{"class":385},[379,1485,1362],{"class":520},[379,1487,771],{"class":385},[379,1489,814],{"class":746},[379,1491,750],{"class":385},[379,1493,819],{"class":753},[379,1495,803],{"class":395},[379,1497,535],{"class":385},[379,1499,826],{"class":753},[379,1501,757],{"class":395},[379,1503,535],{"class":385},[379,1505,762],{"class":753},[379,1507,765],{"class":385},[379,1509,768],{"class":520},[379,1511,771],{"class":385},[379,1513,803],{"class":395},[379,1515,1516],{"class":806},"   \u002F\u002F nil-safe\n",[379,1518,1519,1521,1523,1525,1527,1529,1531,1533,1535,1537,1539,1541,1543,1545,1547,1549,1551,1553,1555],{"class":381,"line":476},[379,1520,743],{"class":385},[379,1522,1408],{"class":385},[379,1524,1411],{"class":753},[379,1526,1414],{"class":385},[379,1528,1362],{"class":520},[379,1530,771],{"class":385},[379,1532,850],{"class":746},[379,1534,750],{"class":385},[379,1536,819],{"class":753},[379,1538,803],{"class":395},[379,1540,535],{"class":385},[379,1542,861],{"class":753},[379,1544,757],{"class":395},[379,1546,535],{"class":385},[379,1548,793],{"class":753},[379,1550,765],{"class":385},[379,1552,798],{"class":520},[379,1554,771],{"class":385},[379,1556,774],{"class":395},[348,1558,1559,1562,1563,1566,1567,1570,1571,1573],{},[365,1560,1561],{},"errors.Is(err, ErrSomething)"," matches any error instantiated from the definition, no matter how deeply wrapped: the same identity semantics as a stdlib sentinel, with the code\u002Fmessage\u002Fstatus set once. Per-call ",[365,1564,1565],{},"attrs"," override the definition's defaults for that occurrence. ",[365,1568,1569],{},"Define"," panics when ",[365,1572,612],{}," is not a 4xx\u002F5xx code (a programmer error that surfaces at init time, not during a request).",[348,1575,1576],{},"The status is decided not at the call site but at the failure mode's declaration, once.",[619,1578,1580],{"id":1579},"the-shop-example-out-of-stock","The shop example: out of stock",[348,1582,1583,1584,1587,1588,1591,1592,1595,1596,1599,1600,1603],{},"The expected business error (",[365,1585,1586],{},"ErrOutOfStock",") is declared where it naturally originates: in the module-level repository. Handlers never import the repository package, though, so the sentinel is re-exported up the chain: ",[365,1589,1590],{},"core"," passes it on (",[365,1593,1594],{},"var ErrOutOfStock = repository.ErrOutOfStock","), and the surface ",[365,1597,1598],{},"service"," does too, so handlers and services only ever check their own surface's service package. The surface-specific wrap error (",[365,1601,1602],{},"ErrPlaceOrder",") lives in the surface service:",[370,1605,1608],{"className":673,"code":1606,"filename":1607,"language":675,"meta":375,"style":375},"package repository\n\nimport (\n    \"net\u002Fhttp\"\n\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Ferrs\"\n)\n\nvar ErrOutOfStock = errs.Define(\"shop_out_of_stock\",\n    errs.Public(\"The product is out of stock.\"),\n    errs.Status(http.StatusConflict))\n","internal\u002Fmodules\u002Fshop\u002Frepository\u002Ferrors.go",[365,1609,1610,1618,1622,1629,1639,1643,1651,1655,1659,1684,1705],{"__ignoreMap":375},[379,1611,1612,1615],{"class":381,"line":382},[379,1613,1614],{"class":385},"package",[379,1616,1617],{"class":520}," repository\n",[379,1619,1620],{"class":381,"line":389},[379,1621,1317],{"emptyLinePlaceholder":1316},[379,1623,1624,1626],{"class":381,"line":416},[379,1625,683],{"class":682},[379,1627,1628],{"class":385}," (\n",[379,1630,1631,1634,1637],{"class":381,"line":437},[379,1632,1633],{"class":385},"    \"",[379,1635,1636],{"class":520},"net\u002Fhttp",[379,1638,691],{"class":385},[379,1640,1641],{"class":381,"line":455},[379,1642,1317],{"emptyLinePlaceholder":1316},[379,1644,1645,1647,1649],{"class":381,"line":476},[379,1646,1633],{"class":385},[379,1648,688],{"class":520},[379,1650,691],{"class":385},[379,1652,1653],{"class":381,"line":497},[379,1654,1311],{"class":385},[379,1656,1657],{"class":381,"line":512},[379,1658,1317],{"emptyLinePlaceholder":1316},[379,1660,1661,1663,1666,1668,1670,1672,1674,1676,1678,1680,1682],{"class":381,"line":575},[379,1662,1285],{"class":385},[379,1664,1665],{"class":966}," ErrOutOfStock ",[379,1667,1291],{"class":385},[379,1669,1325],{"class":966},[379,1671,648],{"class":385},[379,1673,1569],{"class":746},[379,1675,750],{"class":385},[379,1677,399],{"class":385},[379,1679,1082],{"class":407},[379,1681,399],{"class":385},[379,1683,413],{"class":385},[379,1685,1686,1689,1691,1693,1695,1697,1700,1702],{"class":381,"line":581},[379,1687,1688],{"class":966},"    errs",[379,1690,648],{"class":385},[379,1692,608],{"class":746},[379,1694,750],{"class":385},[379,1696,399],{"class":385},[379,1698,1699],{"class":407},"The product is out of stock.",[379,1701,399],{"class":385},[379,1703,1704],{"class":385},"),\n",[379,1706,1708,1710,1712,1714,1716,1718,1720,1722],{"class":381,"line":1707},11,[379,1709,1688],{"class":966},[379,1711,648],{"class":385},[379,1713,612],{"class":746},[379,1715,750],{"class":385},[379,1717,1019],{"class":966},[379,1719,648],{"class":385},[379,1721,1024],{"class":966},[379,1723,1724],{"class":385},"))\n",[370,1726,1729],{"className":673,"code":1727,"filename":1728,"language":675,"meta":375,"style":375},"package service\n\nimport (\n    \"github.com\u002Fgp-system\u002Fgpsystem\u002Ferrs\"\n\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Fcore\"\n)\n\n\u002F\u002F Re-exported from core (which re-exports it from the repository): handlers\n\u002F\u002F only ever import their own surface's service package.\nvar ErrOutOfStock = core.ErrOutOfStock\n\nvar ErrPlaceOrder = errs.Define(\"shop_api_place_order_failed\",\n    errs.Public(\"Placing the order failed, please try again later.\"))\n","internal\u002Fmodules\u002Fshop\u002Fsurfaces\u002Fapi\u002Fservice\u002Ferrors.go",[365,1730,1731,1738,1742,1748,1756,1760,1769,1773,1777,1782,1787,1803,1808,1835],{"__ignoreMap":375},[379,1732,1733,1735],{"class":381,"line":382},[379,1734,1614],{"class":385},[379,1736,1737],{"class":520}," service\n",[379,1739,1740],{"class":381,"line":389},[379,1741,1317],{"emptyLinePlaceholder":1316},[379,1743,1744,1746],{"class":381,"line":416},[379,1745,683],{"class":682},[379,1747,1628],{"class":385},[379,1749,1750,1752,1754],{"class":381,"line":437},[379,1751,1633],{"class":385},[379,1753,688],{"class":520},[379,1755,691],{"class":385},[379,1757,1758],{"class":381,"line":455},[379,1759,1317],{"emptyLinePlaceholder":1316},[379,1761,1762,1764,1767],{"class":381,"line":476},[379,1763,1633],{"class":385},[379,1765,1766],{"class":520},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\u002Fcore",[379,1768,691],{"class":385},[379,1770,1771],{"class":381,"line":497},[379,1772,1311],{"class":385},[379,1774,1775],{"class":381,"line":512},[379,1776,1317],{"emptyLinePlaceholder":1316},[379,1778,1779],{"class":381,"line":575},[379,1780,1781],{"class":806},"\u002F\u002F Re-exported from core (which re-exports it from the repository): handlers\n",[379,1783,1784],{"class":381,"line":581},[379,1785,1786],{"class":806},"\u002F\u002F only ever import their own surface's service package.\n",[379,1788,1789,1791,1793,1795,1798,1800],{"class":381,"line":1707},[379,1790,1285],{"class":385},[379,1792,1665],{"class":966},[379,1794,1291],{"class":385},[379,1796,1797],{"class":966}," core",[379,1799,648],{"class":385},[379,1801,1802],{"class":966},"ErrOutOfStock\n",[379,1804,1806],{"class":381,"line":1805},12,[379,1807,1317],{"emptyLinePlaceholder":1316},[379,1809,1811,1813,1816,1818,1820,1822,1824,1826,1828,1831,1833],{"class":381,"line":1810},13,[379,1812,1285],{"class":385},[379,1814,1815],{"class":966}," ErrPlaceOrder ",[379,1817,1291],{"class":385},[379,1819,1325],{"class":966},[379,1821,648],{"class":385},[379,1823,1569],{"class":746},[379,1825,750],{"class":385},[379,1827,399],{"class":385},[379,1829,1830],{"class":407},"shop_api_place_order_failed",[379,1832,399],{"class":385},[379,1834,413],{"class":385},[379,1836,1838,1840,1842,1844,1846,1848,1851,1853],{"class":381,"line":1837},14,[379,1839,1688],{"class":966},[379,1841,648],{"class":385},[379,1843,608],{"class":746},[379,1845,750],{"class":385},[379,1847,399],{"class":385},[379,1849,1850],{"class":407},"Placing the order failed, please try again later.",[379,1852,399],{"class":385},[379,1854,1724],{"class":385},[348,1856,1857],{},"The error originates in the repository (the stack is captured here), and every layer wraps it upward with its own context:",[370,1859,1862],{"className":673,"code":1860,"filename":1861,"language":675,"meta":375,"style":375},"func (r *Orders) DecrementStock(ctx context.Context, productID string, qty int) error {\n    tag, err := r.db.Exec(ctx, decrementStockSQL, productID, qty)\n    if err != nil {\n        return errs.Wrap(err, \"orders: decrement stock\",\n            errs.With(\"product_id\", productID))\n    }\n    if tag.RowsAffected() == 0 {\n        return ErrOutOfStock.New(\"orders: decrement stock\",\n            errs.With(\"product_id\", productID))\n    }\n    return nil\n}\n","internal\u002Fmodules\u002Fshop\u002Frepository\u002Forders.go",[365,1863,1864,1917,1962,1977,2003,2027,2032,2055,2076,2098,2102,2110],{"__ignoreMap":375},[379,1865,1866,1868,1870,1873,1875,1878,1880,1883,1885,1888,1891,1893,1896,1898,1901,1903,1905,1908,1910,1912,1914],{"class":381,"line":382},[379,1867,743],{"class":385},[379,1869,1408],{"class":385},[379,1871,1872],{"class":753},"r ",[379,1874,1414],{"class":385},[379,1876,1877],{"class":520},"Orders",[379,1879,771],{"class":385},[379,1881,1882],{"class":746}," DecrementStock",[379,1884,750],{"class":385},[379,1886,1887],{"class":753},"ctx",[379,1889,1890],{"class":520}," context",[379,1892,648],{"class":385},[379,1894,1895],{"class":520},"Context",[379,1897,535],{"class":385},[379,1899,1900],{"class":753}," productID",[379,1902,757],{"class":395},[379,1904,535],{"class":385},[379,1906,1907],{"class":753}," qty",[379,1909,1152],{"class":395},[379,1911,771],{"class":385},[379,1913,803],{"class":395},[379,1915,1916],{"class":385}," {\n",[379,1918,1919,1922,1924,1927,1930,1933,1935,1938,1940,1943,1945,1947,1949,1952,1954,1956,1958,1960],{"class":381,"line":389},[379,1920,1921],{"class":966},"    tag",[379,1923,535],{"class":385},[379,1925,1926],{"class":966}," err ",[379,1928,1929],{"class":385},":=",[379,1931,1932],{"class":966}," r",[379,1934,648],{"class":385},[379,1936,1937],{"class":966},"db",[379,1939,648],{"class":385},[379,1941,1942],{"class":746},"Exec",[379,1944,750],{"class":385},[379,1946,1887],{"class":966},[379,1948,535],{"class":385},[379,1950,1951],{"class":966}," decrementStockSQL",[379,1953,535],{"class":385},[379,1955,1900],{"class":966},[379,1957,535],{"class":385},[379,1959,1907],{"class":966},[379,1961,1311],{"class":385},[379,1963,1964,1967,1969,1972,1975],{"class":381,"line":416},[379,1965,1966],{"class":682},"    if",[379,1968,1926],{"class":966},[379,1970,1971],{"class":385},"!=",[379,1973,1974],{"class":385}," nil",[379,1976,1916],{"class":385},[379,1978,1979,1982,1984,1986,1988,1990,1992,1994,1996,1999,2001],{"class":381,"line":437},[379,1980,1981],{"class":682},"        return",[379,1983,1325],{"class":966},[379,1985,648],{"class":385},[379,1987,913],{"class":746},[379,1989,750],{"class":385},[379,1991,819],{"class":966},[379,1993,535],{"class":385},[379,1995,404],{"class":385},[379,1997,1998],{"class":407},"orders: decrement stock",[379,2000,399],{"class":385},[379,2002,413],{"class":385},[379,2004,2005,2008,2010,2012,2014,2016,2019,2021,2023,2025],{"class":381,"line":455},[379,2006,2007],{"class":966},"            errs",[379,2009,648],{"class":385},[379,2011,1038],{"class":746},[379,2013,750],{"class":385},[379,2015,399],{"class":385},[379,2017,2018],{"class":407},"product_id",[379,2020,399],{"class":385},[379,2022,535],{"class":385},[379,2024,1900],{"class":966},[379,2026,1724],{"class":385},[379,2028,2029],{"class":381,"line":476},[379,2030,2031],{"class":385},"    }\n",[379,2033,2034,2036,2039,2041,2044,2047,2050,2053],{"class":381,"line":497},[379,2035,1966],{"class":682},[379,2037,2038],{"class":966}," tag",[379,2040,648],{"class":385},[379,2042,2043],{"class":746},"RowsAffected",[379,2045,2046],{"class":385},"()",[379,2048,2049],{"class":385}," ==",[379,2051,2052],{"class":449}," 0",[379,2054,1916],{"class":385},[379,2056,2057,2059,2062,2064,2066,2068,2070,2072,2074],{"class":381,"line":512},[379,2058,1981],{"class":682},[379,2060,2061],{"class":966}," ErrOutOfStock",[379,2063,648],{"class":385},[379,2065,1299],{"class":746},[379,2067,750],{"class":385},[379,2069,399],{"class":385},[379,2071,1998],{"class":407},[379,2073,399],{"class":385},[379,2075,413],{"class":385},[379,2077,2078,2080,2082,2084,2086,2088,2090,2092,2094,2096],{"class":381,"line":575},[379,2079,2007],{"class":966},[379,2081,648],{"class":385},[379,2083,1038],{"class":746},[379,2085,750],{"class":385},[379,2087,399],{"class":385},[379,2089,2018],{"class":407},[379,2091,399],{"class":385},[379,2093,535],{"class":385},[379,2095,1900],{"class":966},[379,2097,1724],{"class":385},[379,2099,2100],{"class":381,"line":581},[379,2101,2031],{"class":385},[379,2103,2104,2107],{"class":381,"line":1707},[379,2105,2106],{"class":682},"    return",[379,2108,2109],{"class":385}," nil\n",[379,2111,2112],{"class":381,"line":1805},[379,2113,584],{"class":385},[370,2115,2118],{"className":673,"code":2116,"filename":2117,"language":675,"meta":375,"style":375},"func (s *Service) PlaceOrder(ctx context.Context, in PlaceOrderInput) error {\n    err := s.core.PlaceOrder(ctx, in) \u002F\u002F transaction + stock decrement + outbox in core\n    if errors.Is(err, ErrOutOfStock) {\n        return err \u002F\u002F expected business error: passes through unchanged\n    }\n    return ErrPlaceOrder.Wrap(err, \"shop: place order\") \u002F\u002F nil-safe: nil in, nil out\n}\n","internal\u002Fmodules\u002Fshop\u002Fsurfaces\u002Fapi\u002Fservice\u002Fservice.go",[365,2119,2120,2163,2195,2218,2227,2231,2260],{"__ignoreMap":375},[379,2121,2122,2124,2126,2129,2131,2134,2136,2139,2141,2143,2145,2147,2149,2151,2154,2157,2159,2161],{"class":381,"line":382},[379,2123,743],{"class":385},[379,2125,1408],{"class":385},[379,2127,2128],{"class":753},"s ",[379,2130,1414],{"class":385},[379,2132,2133],{"class":520},"Service",[379,2135,771],{"class":385},[379,2137,2138],{"class":746}," PlaceOrder",[379,2140,750],{"class":385},[379,2142,1887],{"class":753},[379,2144,1890],{"class":520},[379,2146,648],{"class":385},[379,2148,1895],{"class":520},[379,2150,535],{"class":385},[379,2152,2153],{"class":753}," in",[379,2155,2156],{"class":520}," PlaceOrderInput",[379,2158,771],{"class":385},[379,2160,803],{"class":395},[379,2162,1916],{"class":385},[379,2164,2165,2168,2170,2173,2175,2177,2179,2182,2184,2186,2188,2190,2192],{"class":381,"line":389},[379,2166,2167],{"class":966},"    err ",[379,2169,1929],{"class":385},[379,2171,2172],{"class":966}," s",[379,2174,648],{"class":385},[379,2176,1590],{"class":966},[379,2178,648],{"class":385},[379,2180,2181],{"class":746},"PlaceOrder",[379,2183,750],{"class":385},[379,2185,1887],{"class":966},[379,2187,535],{"class":385},[379,2189,2153],{"class":966},[379,2191,771],{"class":385},[379,2193,2194],{"class":806}," \u002F\u002F transaction + stock decrement + outbox in core\n",[379,2196,2197,2199,2201,2203,2206,2208,2210,2212,2214,2216],{"class":381,"line":416},[379,2198,1966],{"class":682},[379,2200,1294],{"class":966},[379,2202,648],{"class":385},[379,2204,2205],{"class":746},"Is",[379,2207,750],{"class":385},[379,2209,819],{"class":966},[379,2211,535],{"class":385},[379,2213,2061],{"class":966},[379,2215,771],{"class":385},[379,2217,1916],{"class":385},[379,2219,2220,2222,2224],{"class":381,"line":437},[379,2221,1981],{"class":682},[379,2223,1926],{"class":966},[379,2225,2226],{"class":806},"\u002F\u002F expected business error: passes through unchanged\n",[379,2228,2229],{"class":381,"line":455},[379,2230,2031],{"class":385},[379,2232,2233,2235,2238,2240,2242,2244,2246,2248,2250,2253,2255,2257],{"class":381,"line":476},[379,2234,2106],{"class":682},[379,2236,2237],{"class":966}," ErrPlaceOrder",[379,2239,648],{"class":385},[379,2241,913],{"class":746},[379,2243,750],{"class":385},[379,2245,819],{"class":966},[379,2247,535],{"class":385},[379,2249,404],{"class":385},[379,2251,2252],{"class":407},"shop: place order",[379,2254,399],{"class":385},[379,2256,771],{"class":385},[379,2258,2259],{"class":806}," \u002F\u002F nil-safe: nil in, nil out\n",[379,2261,2262],{"class":381,"line":497},[379,2263,584],{"class":385},[348,2265,2266],{},"The handler just returns what it gets, rendering is not its job:",[370,2268,2271],{"className":673,"code":2269,"filename":2270,"language":675,"meta":375,"style":375},"func (h *Handler) PlaceOrder(ctx context.Context, req gen.PlaceOrderRequest) (gen.PlaceOrderResponse, error) {\n    if err := h.svc.PlaceOrder(ctx, toPlaceOrder(req)); err != nil {\n        return nil, err\n    }\n    return gen.PlaceOrder201Response{}, nil\n}\n","internal\u002Fmodules\u002Fshop\u002Fsurfaces\u002Fapi\u002Fhttp\u002Fhandler.go",[365,2272,2273,2334,2379,2389,2393,2409],{"__ignoreMap":375},[379,2274,2275,2277,2279,2282,2284,2287,2289,2291,2293,2295,2297,2299,2301,2303,2306,2309,2311,2314,2316,2318,2321,2323,2326,2328,2330,2332],{"class":381,"line":382},[379,2276,743],{"class":385},[379,2278,1408],{"class":385},[379,2280,2281],{"class":753},"h ",[379,2283,1414],{"class":385},[379,2285,2286],{"class":520},"Handler",[379,2288,771],{"class":385},[379,2290,2138],{"class":746},[379,2292,750],{"class":385},[379,2294,1887],{"class":753},[379,2296,1890],{"class":520},[379,2298,648],{"class":385},[379,2300,1895],{"class":520},[379,2302,535],{"class":385},[379,2304,2305],{"class":753}," req",[379,2307,2308],{"class":520}," gen",[379,2310,648],{"class":385},[379,2312,2313],{"class":520},"PlaceOrderRequest",[379,2315,771],{"class":385},[379,2317,1408],{"class":385},[379,2319,2320],{"class":520},"gen",[379,2322,648],{"class":385},[379,2324,2325],{"class":520},"PlaceOrderResponse",[379,2327,535],{"class":385},[379,2329,803],{"class":395},[379,2331,771],{"class":385},[379,2333,1916],{"class":385},[379,2335,2336,2338,2340,2342,2345,2347,2350,2352,2354,2356,2358,2360,2363,2365,2368,2371,2373,2375,2377],{"class":381,"line":389},[379,2337,1966],{"class":682},[379,2339,1926],{"class":966},[379,2341,1929],{"class":385},[379,2343,2344],{"class":966}," h",[379,2346,648],{"class":385},[379,2348,2349],{"class":966},"svc",[379,2351,648],{"class":385},[379,2353,2181],{"class":746},[379,2355,750],{"class":385},[379,2357,1887],{"class":966},[379,2359,535],{"class":385},[379,2361,2362],{"class":746}," toPlaceOrder",[379,2364,750],{"class":385},[379,2366,2367],{"class":966},"req",[379,2369,2370],{"class":385},"));",[379,2372,1926],{"class":966},[379,2374,1971],{"class":385},[379,2376,1974],{"class":385},[379,2378,1916],{"class":385},[379,2380,2381,2383,2386],{"class":381,"line":416},[379,2382,1981],{"class":682},[379,2384,2385],{"class":385}," nil,",[379,2387,2388],{"class":966}," err\n",[379,2390,2391],{"class":381,"line":437},[379,2392,2031],{"class":385},[379,2394,2395,2397,2399,2401,2404,2407],{"class":381,"line":455},[379,2396,2106],{"class":682},[379,2398,2308],{"class":520},[379,2400,648],{"class":385},[379,2402,2403],{"class":520},"PlaceOrder201Response",[379,2405,2406],{"class":385},"{},",[379,2408,2109],{"class":385},[379,2410,2411],{"class":381,"line":476},[379,2412,584],{"class":385},[348,2414,2415],{},"And then the three audiences see three things:",[348,2417,2418,2421,2422,2424],{},[352,2419,2420],{},"The client"," (409, because ",[365,2423,1586],{}," is status-mapped):",[370,2426,2428],{"className":372,"code":2427,"language":374,"meta":375,"style":375},"{\n  \"type\": \"about:blank\",\n  \"title\": \"Conflict\",\n  \"status\": 409,\n  \"detail\": \"The product is out of stock.\",\n  \"instance\": \"\u002Fapi\u002Fv1\u002Fshop\u002Forders\",\n  \"code\": \"shop_out_of_stock\"\n}\n",[365,2429,2430,2434,2452,2471,2486,2504,2522,2538],{"__ignoreMap":375},[379,2431,2432],{"class":381,"line":382},[379,2433,386],{"class":385},[379,2435,2436,2438,2440,2442,2444,2446,2448,2450],{"class":381,"line":389},[379,2437,392],{"class":385},[379,2439,396],{"class":395},[379,2441,399],{"class":385},[379,2443,368],{"class":385},[379,2445,404],{"class":385},[379,2447,408],{"class":407},[379,2449,399],{"class":385},[379,2451,413],{"class":385},[379,2453,2454,2456,2458,2460,2462,2464,2467,2469],{"class":381,"line":416},[379,2455,392],{"class":385},[379,2457,421],{"class":395},[379,2459,399],{"class":385},[379,2461,368],{"class":385},[379,2463,404],{"class":385},[379,2465,2466],{"class":407},"Conflict",[379,2468,399],{"class":385},[379,2470,413],{"class":385},[379,2472,2473,2475,2477,2479,2481,2484],{"class":381,"line":437},[379,2474,392],{"class":385},[379,2476,442],{"class":395},[379,2478,399],{"class":385},[379,2480,368],{"class":385},[379,2482,2483],{"class":449}," 409",[379,2485,413],{"class":385},[379,2487,2488,2490,2492,2494,2496,2498,2500,2502],{"class":381,"line":455},[379,2489,392],{"class":385},[379,2491,460],{"class":395},[379,2493,399],{"class":385},[379,2495,368],{"class":385},[379,2497,404],{"class":385},[379,2499,1699],{"class":407},[379,2501,399],{"class":385},[379,2503,413],{"class":385},[379,2505,2506,2508,2510,2512,2514,2516,2518,2520],{"class":381,"line":476},[379,2507,392],{"class":385},[379,2509,481],{"class":395},[379,2511,399],{"class":385},[379,2513,368],{"class":385},[379,2515,404],{"class":385},[379,2517,490],{"class":407},[379,2519,399],{"class":385},[379,2521,413],{"class":385},[379,2523,2524,2526,2528,2530,2532,2534,2536],{"class":381,"line":497},[379,2525,392],{"class":385},[379,2527,365],{"class":395},[379,2529,399],{"class":385},[379,2531,368],{"class":385},[379,2533,404],{"class":385},[379,2535,1082],{"class":407},[379,2537,691],{"class":385},[379,2539,2540],{"class":381,"line":512},[379,2541,584],{"class":385},[348,2543,2544,2547,2548,2551,2552,2554],{},[352,2545,2546],{},"The log",": nothing. A status-mapped 4xx is expected client behavior, not an incident. It produces no ",[365,2549,2550],{},"request failed"," log line. ",[352,2553,237],{},": also nothing, for the same reason.",[348,2556,2557,2558,2560,2561,2564,2565,2567,2568,2570],{},"If, however, Postgres drops out underneath, the chain exits through ",[365,2559,1602],{}," as a 500: the client gets only the ",[365,2562,2563],{},"\"Placing the order failed...\""," detail and the ",[365,2566,1830],{}," code, the log gets the full chain and stack, and Sentry gets an issue titled ",[365,2569,1830],{},", grouped by code and linked to the trace.",[619,2572,2574],{"id":2573},"the-mapping","The mapping",[348,2576,2577,2578,2581,2582,2585],{},"The central mapping (",[365,2579,2580],{},"httperr.NewErrorHandler"," on Fiber; ",[365,2583,2584],{},"httperr.WriteError"," on chi; wired into the generated server's error hooks and the router's panic\u002F404\u002F405 handlers) decides in this order:",[2587,2588,2589,2602],"table",{},[2590,2591,2592],"thead",{},[2593,2594,2595,2599],"tr",{},[2596,2597,2598],"th",{},"Error",[2596,2600,2601],{},"Response",[2603,2604,2605,2616,2630,2641,2651,2662,2682],"tbody",{},[2593,2606,2607,2613],{},[2608,2609,2610],"td",{},[365,2611,2612],{},"*httperr.Problem",[2608,2614,2615],{},"rendered as-is",[2593,2617,2618,2621],{},[2608,2619,2620],{},"a validation error from bind",[2608,2622,2623,2624,2627,2628,771],{},"422 with per-field ",[365,2625,2626],{},"errors[]"," (see ",[357,2629,123],{"href":124},[2593,2631,2632,2638],{},[2608,2633,2634,2637],{},[365,2635,2636],{},"*fiber.BindError"," (malformed body\u002Fparams)",[2608,2639,2640],{},"400",[2593,2642,2643,2648],{},[2608,2644,2645],{},[365,2646,2647],{},"*fiber.Error",[2608,2649,2650],{},"its own status code",[2593,2652,2653,2659],{},[2608,2654,2655,2658],{},[365,2656,2657],{},"*http.MaxBytesError"," (body over the limit)",[2608,2660,2661],{},"413",[2593,2663,2664,2670],{},[2608,2665,2666,2667,2669],{},"an ",[365,2668,598],{}," error in the chain",[2608,2671,2672,2675,2676,2678,2679,2681],{},[365,2673,2674],{},"errs.StatusOf"," (default 500), ",[365,2677,460],{}," = the public message, ",[365,2680,365],{}," member = the code",[2593,2683,2684,2687],{},[2608,2685,2686],{},"anything else",[2608,2688,2689],{},"500, no detail",[348,2691,2692,2693,2696,2697,2700,2701,2703,2704,2706,2707,609,2709,2711,2712,2714],{},"On a 5xx the underlying error goes to ",[365,2694,2695],{},"slog"," ",[352,2698,2699],{},"with the request's trace context",". The client gets no internals. In dev mode (",[365,2702,658],{},") the detail is filled in for debugging, and when the chain carries an ",[365,2705,598],{}," error the response also gets ",[365,2708,662],{},[365,2710,665],{}," extension members. The full wire-side picture is on the ",[357,2713,118],{"href":119}," page.",[348,2716,2717,2718,2720,2721,2724,2725,2724,2728,2731,2732,2735,2736,2738,2739,2742],{},"For quick 4xx cases the ",[365,2719,640],{}," constructors remain available (",[365,2722,2723],{},"httperr.NotFound(...)",", ",[365,2726,2727],{},"httperr.Conflict(...)",[365,2729,2730],{},"httperr.Validation(...)","), but as soon as a failure mode has a name, ",[365,2733,2734],{},"errs.Define"," is the better home: recognition (",[365,2737,725],{},") and rendering (status, code, message) live in a single declaration, not in an ",[365,2740,2741],{},"if"," branch in a handler.",[619,2744,2746],{"id":2745},"log-output","Log output",[348,2748,2749,2752,2753,2756,2757,2760,2761,2763],{},[365,2750,2751],{},"*errs.Error"," implements ",[365,2754,2755],{},"slog.LogValuer",", so ",[365,2758,2759],{},"slog.Any(\"error\", err)"," emits a structured group instead of one flattened string. ",[365,2762,640],{}," does this automatically on every 5xx:",[370,2765,2767],{"className":372,"code":2766,"language":374,"meta":375,"style":375},"\"error\": {\n  \"msg\": \"shop: place order: orders: decrement stock: connection refused\",\n  \"code\": \"shop_api_place_order_failed\",\n  \"public\": \"Placing the order failed, please try again later.\",\n  \"chain\": [\n    { \"msg\": \"shop: place order\",\n      \"file\": \"api\u002Fservice\u002Fservice.go\", \"line\": 42, \"function\": \"service.(*Service).PlaceOrder\",\n      \"code\": \"shop_api_place_order_failed\" },\n    { \"msg\": \"orders: decrement stock\",\n      \"file\": \"repository\u002Forders.go\", \"line\": 31, \"function\": \"repository.(*Orders).DecrementStock\",\n      \"meta\": { \"product_id\": \"prod_42\" } },\n    { \"msg\": \"connection refused\" }\n  ],\n  \"stack\": [\n    { \"file\": \"repository\u002Forders.go\", \"line\": 31, \"function\": \"repository.(*Orders).DecrementStock\" },\n    { \"file\": \"api\u002Fservice\u002Fservice.go\", \"line\": 42, \"function\": \"service.(*Service).PlaceOrder\" },\n    { \"file\": \"api\u002Fhttp\u002Fhandler.go\", \"line\": 55, \"function\": \"http.(*Handler).PlaceOrder\" }\n  ]\n}\n",[365,2768,2769,2782,2801,2819,2837,2849,2869,2921,2940,2960,3009,3043,3064,3069,3081,3130,3179,3231,3236],{"__ignoreMap":375},[379,2770,2771,2773,2775,2777,2780],{"class":381,"line":382},[379,2772,399],{"class":385},[379,2774,936],{"class":407},[379,2776,399],{"class":385},[379,2778,2779],{"class":966},": ",[379,2781,386],{"class":385},[379,2783,2784,2786,2788,2790,2792,2794,2797,2799],{"class":381,"line":389},[379,2785,392],{"class":385},[379,2787,754],{"class":395},[379,2789,399],{"class":385},[379,2791,368],{"class":385},[379,2793,404],{"class":385},[379,2795,2796],{"class":407},"shop: place order: orders: decrement stock: connection refused",[379,2798,399],{"class":385},[379,2800,413],{"class":385},[379,2802,2803,2805,2807,2809,2811,2813,2815,2817],{"class":381,"line":416},[379,2804,392],{"class":385},[379,2806,365],{"class":395},[379,2808,399],{"class":385},[379,2810,368],{"class":385},[379,2812,404],{"class":385},[379,2814,1830],{"class":407},[379,2816,399],{"class":385},[379,2818,413],{"class":385},[379,2820,2821,2823,2825,2827,2829,2831,2833,2835],{"class":381,"line":437},[379,2822,392],{"class":385},[379,2824,632],{"class":395},[379,2826,399],{"class":385},[379,2828,368],{"class":385},[379,2830,404],{"class":385},[379,2832,1850],{"class":407},[379,2834,399],{"class":385},[379,2836,413],{"class":385},[379,2838,2839,2841,2843,2845,2847],{"class":381,"line":455},[379,2840,392],{"class":385},[379,2842,665],{"class":395},[379,2844,399],{"class":385},[379,2846,368],{"class":385},[379,2848,509],{"class":385},[379,2850,2851,2853,2855,2857,2859,2861,2863,2865,2867],{"class":381,"line":476},[379,2852,515],{"class":385},[379,2854,404],{"class":385},[379,2856,754],{"class":520},[379,2858,399],{"class":385},[379,2860,368],{"class":385},[379,2862,404],{"class":385},[379,2864,2252],{"class":407},[379,2866,399],{"class":385},[379,2868,413],{"class":385},[379,2870,2871,2874,2877,2879,2881,2883,2886,2888,2890,2892,2894,2896,2898,2901,2903,2905,2908,2910,2912,2914,2917,2919],{"class":381,"line":497},[379,2872,2873],{"class":385},"      \"",[379,2875,2876],{"class":520},"file",[379,2878,399],{"class":385},[379,2880,368],{"class":385},[379,2882,404],{"class":385},[379,2884,2885],{"class":407},"api\u002Fservice\u002Fservice.go",[379,2887,399],{"class":385},[379,2889,535],{"class":385},[379,2891,404],{"class":385},[379,2893,381],{"class":520},[379,2895,399],{"class":385},[379,2897,368],{"class":385},[379,2899,2900],{"class":449}," 42",[379,2902,535],{"class":385},[379,2904,404],{"class":385},[379,2906,2907],{"class":520},"function",[379,2909,399],{"class":385},[379,2911,368],{"class":385},[379,2913,404],{"class":385},[379,2915,2916],{"class":407},"service.(*Service).PlaceOrder",[379,2918,399],{"class":385},[379,2920,413],{"class":385},[379,2922,2923,2925,2927,2929,2931,2933,2935,2937],{"class":381,"line":512},[379,2924,2873],{"class":385},[379,2926,365],{"class":520},[379,2928,399],{"class":385},[379,2930,368],{"class":385},[379,2932,404],{"class":385},[379,2934,1830],{"class":407},[379,2936,399],{"class":385},[379,2938,2939],{"class":385}," },\n",[379,2941,2942,2944,2946,2948,2950,2952,2954,2956,2958],{"class":381,"line":575},[379,2943,515],{"class":385},[379,2945,404],{"class":385},[379,2947,754],{"class":520},[379,2949,399],{"class":385},[379,2951,368],{"class":385},[379,2953,404],{"class":385},[379,2955,1998],{"class":407},[379,2957,399],{"class":385},[379,2959,413],{"class":385},[379,2961,2962,2964,2966,2968,2970,2972,2975,2977,2979,2981,2983,2985,2987,2990,2992,2994,2996,2998,3000,3002,3005,3007],{"class":381,"line":581},[379,2963,2873],{"class":385},[379,2965,2876],{"class":520},[379,2967,399],{"class":385},[379,2969,368],{"class":385},[379,2971,404],{"class":385},[379,2973,2974],{"class":407},"repository\u002Forders.go",[379,2976,399],{"class":385},[379,2978,535],{"class":385},[379,2980,404],{"class":385},[379,2982,381],{"class":520},[379,2984,399],{"class":385},[379,2986,368],{"class":385},[379,2988,2989],{"class":449}," 31",[379,2991,535],{"class":385},[379,2993,404],{"class":385},[379,2995,2907],{"class":520},[379,2997,399],{"class":385},[379,2999,368],{"class":385},[379,3001,404],{"class":385},[379,3003,3004],{"class":407},"repository.(*Orders).DecrementStock",[379,3006,399],{"class":385},[379,3008,413],{"class":385},[379,3010,3011,3013,3016,3018,3020,3023,3025,3027,3029,3031,3033,3036,3038,3041],{"class":381,"line":1707},[379,3012,2873],{"class":385},[379,3014,3015],{"class":520},"meta",[379,3017,399],{"class":385},[379,3019,368],{"class":385},[379,3021,3022],{"class":385}," {",[379,3024,404],{"class":385},[379,3026,2018],{"class":449},[379,3028,399],{"class":385},[379,3030,368],{"class":385},[379,3032,404],{"class":385},[379,3034,3035],{"class":407},"prod_42",[379,3037,399],{"class":385},[379,3039,3040],{"class":385}," }",[379,3042,2939],{"class":385},[379,3044,3045,3047,3049,3051,3053,3055,3057,3060,3062],{"class":381,"line":1805},[379,3046,515],{"class":385},[379,3048,404],{"class":385},[379,3050,754],{"class":520},[379,3052,399],{"class":385},[379,3054,368],{"class":385},[379,3056,404],{"class":385},[379,3058,3059],{"class":407},"connection refused",[379,3061,399],{"class":385},[379,3063,572],{"class":385},[379,3065,3066],{"class":381,"line":1810},[379,3067,3068],{"class":385},"  ],\n",[379,3070,3071,3073,3075,3077,3079],{"class":381,"line":1837},[379,3072,392],{"class":385},[379,3074,662],{"class":395},[379,3076,399],{"class":385},[379,3078,368],{"class":385},[379,3080,509],{"class":385},[379,3082,3084,3086,3088,3090,3092,3094,3096,3098,3100,3102,3104,3106,3108,3110,3112,3114,3116,3118,3120,3122,3124,3126,3128],{"class":381,"line":3083},15,[379,3085,515],{"class":385},[379,3087,404],{"class":385},[379,3089,2876],{"class":520},[379,3091,399],{"class":385},[379,3093,368],{"class":385},[379,3095,404],{"class":385},[379,3097,2974],{"class":407},[379,3099,399],{"class":385},[379,3101,535],{"class":385},[379,3103,404],{"class":385},[379,3105,381],{"class":520},[379,3107,399],{"class":385},[379,3109,368],{"class":385},[379,3111,2989],{"class":449},[379,3113,535],{"class":385},[379,3115,404],{"class":385},[379,3117,2907],{"class":520},[379,3119,399],{"class":385},[379,3121,368],{"class":385},[379,3123,404],{"class":385},[379,3125,3004],{"class":407},[379,3127,399],{"class":385},[379,3129,2939],{"class":385},[379,3131,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3155,3157,3159,3161,3163,3165,3167,3169,3171,3173,3175,3177],{"class":381,"line":3132},16,[379,3134,515],{"class":385},[379,3136,404],{"class":385},[379,3138,2876],{"class":520},[379,3140,399],{"class":385},[379,3142,368],{"class":385},[379,3144,404],{"class":385},[379,3146,2885],{"class":407},[379,3148,399],{"class":385},[379,3150,535],{"class":385},[379,3152,404],{"class":385},[379,3154,381],{"class":520},[379,3156,399],{"class":385},[379,3158,368],{"class":385},[379,3160,2900],{"class":449},[379,3162,535],{"class":385},[379,3164,404],{"class":385},[379,3166,2907],{"class":520},[379,3168,399],{"class":385},[379,3170,368],{"class":385},[379,3172,404],{"class":385},[379,3174,2916],{"class":407},[379,3176,399],{"class":385},[379,3178,2939],{"class":385},[379,3180,3182,3184,3186,3188,3190,3192,3194,3197,3199,3201,3203,3205,3207,3209,3212,3214,3216,3218,3220,3222,3224,3227,3229],{"class":381,"line":3181},17,[379,3183,515],{"class":385},[379,3185,404],{"class":385},[379,3187,2876],{"class":520},[379,3189,399],{"class":385},[379,3191,368],{"class":385},[379,3193,404],{"class":385},[379,3195,3196],{"class":407},"api\u002Fhttp\u002Fhandler.go",[379,3198,399],{"class":385},[379,3200,535],{"class":385},[379,3202,404],{"class":385},[379,3204,381],{"class":520},[379,3206,399],{"class":385},[379,3208,368],{"class":385},[379,3210,3211],{"class":449}," 55",[379,3213,535],{"class":385},[379,3215,404],{"class":385},[379,3217,2907],{"class":520},[379,3219,399],{"class":385},[379,3221,368],{"class":385},[379,3223,404],{"class":385},[379,3225,3226],{"class":407},"http.(*Handler).PlaceOrder",[379,3228,399],{"class":385},[379,3230,572],{"class":385},[379,3232,3234],{"class":381,"line":3233},18,[379,3235,578],{"class":385},[379,3237,3239],{"class":381,"line":3238},19,[379,3240,584],{"class":385},[348,3242,3243,3245,3246,3248],{},[365,3244,665],{}," is one object per wrap level (the level's own message, the wrap site, and the code\u002Fmetadata set there), ending in the root cause. ",[365,3247,662],{}," is the full call path from the point of origin, intermediate non-wrapping calls included.",[3250,3251,3252,3253,3256,3257,3259],"warning",{},"Log ",[352,3254,3255],{},"or"," return, not both. ",[365,3258,640],{}," already logs every 5xx with the full chain and stack; if you log it yourself too, you get duplicate records.",[348,3261,3262,3265,3266,3268,3269,2724,3271,3273,3274,3277,3278,3280,3281,3284,3285,3287],{},[365,3263,3264],{},"LogValue"," only runs on the outermost error. If your last wrap of an ",[365,3267,598],{}," error is a plain ",[365,3270,699],{},[365,3272,2695],{}," sees a string and the structure is lost: at a boundary, make ",[365,3275,3276],{},"errs.Wrap"," the outermost wrap. (",[365,3279,640],{}," defends against this: it logs a separate ",[365,3282,3283],{},"cause"," attribute when it finds an ",[365,3286,598],{}," error buried under plain wrappers.)",[619,3289,237],{"id":3290},"sentry",[348,3292,3293,3295,3296,3299,3300,3303,3304,3306,3307,3309],{},[365,3294,2751],{}," exposes a ",[365,3297,3298],{},"StackTrace() []uintptr"," method (the shape sentry-go recognizes via reflection), so the kit's ",[357,3301,3302],{"href":238},"Sentry integration"," extracts the stack with no extra wiring. Before sending, the integration retitles the exception with the outermost error's ",[365,3305,616],{}," (when set), so the code is not just the response's ",[365,3308,365],{}," member but also the Sentry issue's title and grouping fingerprint: one failure mode = one issue, regardless of message-text changes.",[619,3311,3313],{"id":3312},"the-spec-side","The spec side",[348,3315,3316,3317,3320,3321,3323,3324,3327,3328,609,3330,3332,3333,3336,3337,3340],{},"The generated ",[365,3318,3319],{},"spec\u002Ftypespec\u002Fshared\u002Ferrors.tsp"," mirrors ",[365,3322,592],{}," (including ",[365,3325,3326],{},"FieldError","), so frontend clients generated from the OpenAPI get exactly this shape. If you extend one, extend the other. The dev-only ",[365,3329,662],{},[365,3331,665],{}," members are deliberately ",[352,3334,3335],{},"not"," mirrored in ",[365,3338,3339],{},"errors.tsp",": they never appear in production, so no client can build on them.",[619,3342,3344],{"id":3343},"patterns-used","Patterns used",[348,3346,3347,3348,3351,3352,1408,3357,3359,3360,1408,3363,3366,3367,3369,3370,3372,3373,2714],{},"This page is the full write-up of two patterns also listed in the ",[357,3349,92],{"href":3350},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#error-handling"," catalog: the ",[352,3353,3354,3355],{},"sentinel definition with a custom ",[365,3356,2205],{},[365,3358,2734],{},") and ",[352,3361,3362],{},"lazy, once-per-chain stack capture",[365,3364,3365],{},"errs\u002Fstack.go","). The cascading ",[365,3368,729],{}," mapping (on the ",[365,3371,640],{}," side) and the RFC 9457 shape live on the ",[357,3374,118],{"href":119},[3376,3377,3378],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":375,"searchDepth":389,"depth":389,"links":3380},[3381,3382,3388,3389,3390,3391,3392,3393,3394],{"id":621,"depth":389,"text":622},{"id":669,"depth":389,"text":670,"children":3383},[3384,3385,3386,3387],{"id":952,"depth":416,"text":953},{"id":1086,"depth":416,"text":1087},{"id":1213,"depth":416,"text":1214},{"id":1263,"depth":416,"text":1264},{"id":1351,"depth":389,"text":1352},{"id":1579,"depth":389,"text":1580},{"id":2573,"depth":389,"text":2574},{"id":2745,"depth":389,"text":2746},{"id":3290,"depth":389,"text":237},{"id":3312,"depth":389,"text":3313},{"id":3343,"depth":389,"text":3344},"RFC 9457 problem details everywhere, with errs errors carrying stacks, codes and client-safe messages underneath.","md",null,{},{"icon":81},{"title":78,"description":3395},"rX50zzLehOntR8arVrSNkxqRtoEXsHTWRzm9VxzlRVQ",[3403,3405],{"title":73,"path":74,"stem":75,"description":3404,"icon":76,"children":-1},"The app package: the signal-driven graceful shutdown every gpsystem binary sits on.",{"title":52,"path":83,"stem":84,"description":3406,"icon":85,"children":-1},"Typed configuration from environment variables: envconf, struct composition and the .env file.",1784668706316]