{"record":{"id":"a19b8fa100912a18","repo":"unknwon/the-way-to-go_ZH_CN","slug":"pkg-v","errorCode":null,"errorMessage":"pkg: %v","messagePattern":"pkg: %v","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"eBook/13.4.md","lineNumber":46,"sourceCode":"type ParseError struct {\n    Index int      // The index into the space-separated list of words.\n    Word  string   // The word that generated the parse error.\n    Err error // The raw error that precipitated this error, if any.\n}\n\n// String returns a human-readable error message.\nfunc (e *ParseError) String() string {\n    return fmt.Sprintf(\"pkg parse: error parsing %q as int\", e.Word)\n}\n\n// Parse parses the space-separated words in in put as integers.\nfunc Parse(input string) (numbers []int, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            var ok bool\n            err, ok = r.(error)\n            if !ok {\n                err = fmt.Errorf(\"pkg: %v\", r)\n            }\n        }\n    }()\n\n    fields := strings.Fields(input)\n    numbers = fields2numbers(fields)\n    return\n}\n\nfunc fields2numbers(fields []string) (numbers []int) {\n    if len(fields) == 0 {\n        panic(\"no words to parse\")\n    }\n    for idx, field := range fields {\n        num, err := strconv.Atoi(field)\n        if err != nil {\n            panic(&ParseError{idx, field, err})\n        }","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/unknwon/the-way-to-go_ZH_CN/blob/7a54d34d3657084b6a59e5618bd069b912d571aa/eBook/13.4.md#L28-L64","documentation":"Safety net inside Parse in section 13.4: a deferred recover() catches any panic from fields2numbers; if the recovered value already implements error it is used as-is, otherwise it is wrapped as fmt.Errorf(\"pkg: %v\", r) so Parse always returns an error-typed value. This message therefore means: a helper panicked with a non-error value — in this listing the string panic(\"no words to parse\") on empty input. Note that as printed ParseError implements String() (older idiom), not Error(), so even typed panics take the \"pkg: %v\" wrap.","triggerScenarios":"Parse(\"\") → fields is empty → fields2numbers panics \"no words to parse\" (a string) → the r.(error) assertion fails → err becomes 'pkg: no words to parse'. Likewise any panic carrying a non-error value (int, plain string) from deeper helpers; Parse(\"1 2 three\") panics with a *ParseError which is stringified through the same wrap in this version.","commonSituations":"Converting panic-style internal code to error-returning APIs at a package boundary; batch processors where one bad record must not kill the run; maintainers panicking with strings; debugging why the returned error is an opaque 'pkg: ...' wrapper instead of a typed value that carries Word/Index fields.","solutions":["Handle empty input at the source without panicking: if len(fields) == 0 { return nil, errors.New(\"pkg: no words to parse\") } — keep panic/recover for truly exceptional paths","Panic with error values, not strings: panic(fmt.Errorf(...)) or panic(&ParseError{...}) — and give ParseError an Error() string method so the r.(error) branch preserves its type","On the caller side, unwrap to classify: var pe *ParseError; if errors.As(err, &pe) { use pe.Word } — the 'pkg: %v' text only carries a string","Preserve cause when wrapping: fmt.Errorf(\"pkg: %w\", e) in the error branch so errors.Is/As chain through the wrapper"],"exampleFix":"// before\npanic(\"no words to parse\") // recovered → err = \"pkg: no words to parse\" (untyped string)\n\n// after\nif len(fields) == 0 {\n\treturn nil, errors.New(\"pkg: no words to parse\")\n}\n// and panic only with typed errors:\npanic(&ParseError{Index: idx, Word: s, Err: err})","handlingStrategy":"try-catch","validationCode":"fields := strings.Fields(input)\nif len(fields) == 0 {\n\treturn nil, errors.New(\"pkg: no words to parse\") // pre-check: skip the panic path\n}\nreturn Parse(input)","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\tif e, ok := r.(error); ok {\n\t\t\terr = e\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"pkg: %v\", r)\n\t\t}\n\t}\n}()","preventionTips":["Reserve panic for programmer errors; expected failures (bad input) should return errors directly","When you must recover, panic with values implementing error so the type assertion preserves structure","Wrap recovered errors with %w so errors.As can still find typed causes","Validate obvious cases (empty input, non-numeric tokens) before the parsing core so the panic path stays exceptional"],"tags":["go","panic","recover","error-wrapping","parser"],"backgroundTag":null,"analyzedSha":"7a54d34d3657084b6a59e5618bd069b912d571aa","analyzedAt":"2026-08-15T15:13:06.026Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}