{"record":{"id":"080b7927f2e3c547","repo":"sickn33/agentic-awesome-skills","slug":"id-required-080b79","errorCode":null,"errorMessage":"ID required","messagePattern":"ID required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"skills/fp-ts-errors/SKILL.md","lineNumber":36,"sourceCode":"- When accumulating multiple validation errors\n\nThe 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) {","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/fp-ts-errors/SKILL.md#L18-L54","documentation":"The first throw in fp-ts-errors' motivating example: getUser is called with a falsy id (empty string, null, undefined) before any DB lookup happens. The skill uses it to show that the signature getUser(id: string): User hides two distinct failure modes from callers.","triggerScenarios":"Calling getUser(''), getUser(null as any), or a value from an unvalidated route param where the id never got populated.","commonSituations":"Optional route/query params reaching services unchecked; destructured config fields that are undefined; test helpers calling with empty fixtures.","solutions":["Validate ids at the boundary (non-empty string check) before calling services","Make the signature honest: return Either<DomainError, User> as the skill teaches","Use a NonEmptyString branded type for ids so empty ids fail to compile"],"exampleFix":"// before\nfunction getUser(id: string): User {\n  if (!id) throw new Error('ID required')\n  // ...\n}\n\n// after\nconst getUser = (id: string): E.Either<DomainError, User> =>\n  id.trim() === ''\n    ? E.left({ _tag: 'InvalidId' })\n    : E.right(db.find(id) as User)","handlingStrategy":"validation","validationCode":"const isValidId = (id: unknown): id is string =>\n  typeof id === 'string' && id.trim().length > 0","typeGuard":"const isNonEmptyString = (s: unknown): s is string =>\n  typeof s === 'string' && s.length > 0","tryCatchPattern":null,"preventionTips":["Validate route/query params before service calls","Brand ids as NonEmptyString so empty ids fail at compile time"],"tags":["validation","typescript","fp-ts","required-field"],"backgroundTag":"invalid-argument-value","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}