{"record":{"id":"7e4c4733216bec82","repo":"affaan-m/ECC","slug":"resource-not-found","errorCode":null,"errorMessage":"resource not found","messagePattern":"resource not found","errorType":"error_code","errorClass":"ErrNotFound","httpStatus":null,"severity":"warning","filePath":"skills/golang-patterns/SKILL.md","lineNumber":130,"sourceCode":"}\n```\n\n### Custom Error Types\n\n```go\n// Define domain-specific errors\ntype ValidationError struct {\n    Field   string\n    Message string\n}\n\nfunc (e *ValidationError) Error() string {\n    return fmt.Sprintf(\"validation failed on %s: %s\", e.Field, e.Message)\n}\n\n// Sentinel errors for common cases\nvar (\n    ErrNotFound     = errors.New(\"resource not found\")\n    ErrUnauthorized = errors.New(\"unauthorized\")\n    ErrInvalidInput = errors.New(\"invalid input\")\n)\n```\n\n### Error Checking with errors.Is and errors.As\n\n```go\nfunc HandleError(err error) {\n    // Check for specific error\n    if errors.Is(err, sql.ErrNoRows) {\n        log.Println(\"No records found\")\n        return\n    }\n\n    // Check for error type\n    var validationErr *ValidationError\n    if errors.As(err, &validationErr) {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/golang-patterns/SKILL.md#L112-L148","documentation":"A Go sentinel error ErrNotFound = errors.New(\"resource not found\") from the golang-patterns skill. It is the generic 'row/resource does not exist' sentinel for the domain layer, intended to be matched with errors.Is and wrapped with per-call context via fmt.Errorf. Compared to error-handling's variant, the message is the more descriptive \"resource not found\".","triggerScenarios":"Any repository/service lookup that returns no row wraps ErrNotFound. The handler checks errors.Is(err, ErrNotFound) to respond 404. Also used in switch statements branching on errors.Is against sql.ErrNoRows.","commonSituations":"REST GET /resource/{id} where id does not exist; concurrent delete between read and update; id derived from untrusted input that does not correspond to a real record; pagination cursor pointing past the last page.","solutions":["Wrap with context at every layer: fmt.Errorf(\"get order %s: %w\", id, ErrNotFound) so logs are actionable.","Map to 404 at the HTTP boundary using errors.Is, never 500.","Validate id format before querying (return 400 for malformed ids) to keep 404 semantically 'well-formed but absent'.","Return a generic message to the client; do not confirm which ids exist."],"exampleFix":"// before\nvar ErrNotFound = errors.New(\"resource not found\")\n\n// after: keep sentinel stable; add typed errors for finer handling\ntype NotFoundError struct{ Resource, ID string }\nfunc (e *NotFoundError) Error() string { return e.Resource + \" \" + e.ID + \" not found\" }\n// handlers can now use errors.As(err, &NotFoundError{}) to include the resource name","handlingStrategy":"validation","validationCode":"if valid, _ := isWellFormedID(id); !valid {\n    return httpError(400, \"invalid id\")\n}\n_, err := repo.FindByID(ctx, id)\nif errors.Is(err, ErrNotFound) {\n    return httpError(404, \"not found\")\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Wrap with per-call context (entity + id) at every layer.","Map ErrNotFound to 404 at the handler, never 500.","Validate id shape before querying."],"tags":["go","sentinel-error","not-found","repository"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}