{"record":{"id":"3d5b7fae5617fe44","repo":"sickn33/agentic-awesome-skills","slug":"user-not-found-3d5b7f","errorCode":null,"errorMessage":"User not found","messagePattern":"User not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/fp-ts-errors/SKILL.md","lineNumber":38,"sourceCode":"The core idea: **Errors are just data**. Instead of throwing them into the void and hoping someone catches them, return them as values that TypeScript can track.\n\n---\n\n## 1. Stop Throwing Everywhere\n\n### The Problem with Exceptions\n\nExceptions are invisible in your types. They break the contract between functions.\n\n```typescript\n// What this function signature promises:\nfunction getUser(id: string): User\n\n// What it actually does:\nfunction getUser(id: string): User {\n  if (!id) throw new Error('ID required')\n  const user = db.find(id)\n  if (!user) throw new Error('User not found')\n  return user\n}\n\n// The caller has no idea this can fail\nconst user = getUser(id) // Might explode!\n```\n\nYou end up with code like this:\n\n```typescript\n// MESSY: try/catch everywhere\nfunction processOrder(orderId: string) {\n  let order\n  try {\n    order = getOrder(orderId)\n  } catch (e) {\n    console.error('Failed to get order')\n    return null","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/fp-ts-errors/SKILL.md#L20-L56","documentation":"The second throw in fp-ts-errors' example: db.find(id) returned nothing for a well-formed id. The skill's point is that callers cannot see this failure in the type and must either wrap every call in try/catch or risk runtime crashes.","triggerScenarios":"Calling getUser with a syntactically valid id that has no row: deleted users, expired sessions referencing purged accounts, or lookups against the wrong database/environment.","commonSituations":"Session stores holding user ids after account deletion; multi-tenant systems queried without tenant context; 404 handling missing so this throw becomes an unhandled 500.","solutions":["Return Either/Error union from getUser so absence is a typed value (the skill's core fix)","Handle the not-found case explicitly at the caller (render 404, clear session) rather than catching a generic Error","Add tenant/scope filters so the lookup queries the right data set"],"exampleFix":"// before\nconst user = db.find(id)\nif (!user) throw new Error('User not found')\nreturn user\n\n// after\ntype DomainError = { _tag: 'UserNotFound' } | { _tag: 'InvalidId' }\nconst getUser = (id: string): E.Either<DomainError, User> => {\n  if (!id) return E.left({ _tag: 'InvalidId' })\n  const user = db.find(id)\n  return user ? E.right(user) : E.left({ _tag: 'UserNotFound' })\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const user = getUser(id)\n} catch (e) {\n  if (e instanceof Error && e.message === 'User not found') {\n    // 404 / clear stale session\n  } else throw e\n}","preventionTips":["Prefer Either-returning APIs so absence is a value, not an exception","Purge session references when accounts are deleted","Scope DB lookups with tenant context"],"tags":["not-found","typescript","fp-ts","database-lookup"],"backgroundTag":"resource-not-found","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}