{"record":{"id":"63bcccf1472e517c","repo":"mckaywrigley/chatbot-ui","slug":"error-message-63bccc","errorCode":null,"errorMessage":"error.message","messagePattern":"error\\.message","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"db/presets.ts","lineNumber":12,"sourceCode":"import { supabase } from \"@/lib/supabase/browser-client\"\nimport { TablesInsert, TablesUpdate } from \"@/supabase/types\"\n\nexport const getPresetById = async (presetId: string) => {\n  const { data: preset, error } = await supabase\n    .from(\"presets\")\n    .select(\"*\")\n    .eq(\"id\", presetId)\n    .single()\n\n  if (!preset) {\n    throw new Error(error.message)\n  }\n\n  return preset\n}\n\nexport const getPresetWorkspacesByWorkspaceId = async (workspaceId: string) => {\n  const { data: workspace, error } = await supabase\n    .from(\"workspaces\")\n    .select(\n      `\n      id,\n      name,\n      presets (*)\n    `\n    )\n    .eq(\"id\", workspaceId)\n    .single()\n","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/mckaywrigley/chatbot-ui/blob/81328b61d2a4ab597a7a057be70e785cf756d9f8/db/presets.ts#L1-L30","documentation":"Thrown by `getPresetById` when `.single()` returns no row — the code checks `if (!preset)` and throws `error.message`, which for a missing row is PGRST116 (\"JSON object requested, multiple (or no) rows returned\") or a null message. It conflates 'not found' with genuine query failures because it tests the data, not the error object.","triggerScenarios":"Fetching a preset id that doesn't exist, was deleted, or is filtered out by row-level security for the current user; also fires on genuine query errors (bad column in select, network failure) since only `preset` is checked.","commonSituations":"Deep links to a removed preset, stale ids in client cache, RLS hiding other users' presets, or a typo in the `select(\"*\")` / embedded relation string causing PostgREST to error with null data.","solutions":["Check `error` first and treat code PGRST116 / null preset as a typed NotFound case","Verify the preset id exists with the same role/credentials the app uses","If an embedded relation is selected, ensure the FK and relation name are valid","Return null or a domain-specific NotFoundError instead of throwing raw error.message"],"exampleFix":"// before\nif (!preset) throw new Error(error.message)\n\n// after\nif (error) throw new Error(`getPresetById failed (${error.code}): ${error.message}`)\nif (!preset) throw new NotFoundError(`Preset ${presetId} not found`)\n// or: return preset ?? null and let callers handle absence","handlingStrategy":"type-guard","validationCode":"const isUuid = (v: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)\nif (!isUuid(presetId)) return null","typeGuard":"const isPreset = (x: unknown): x is Preset =>\n  typeof x === \"object\" && x !== null && \"id\" in x && \"user_id\" in x","tryCatchPattern":"try {\n  return await getPresetById(presetId)\n} catch (e) {\n  const msg = String(e?.message ?? e)\n  if (msg.includes(\"0 rows\") || msg.includes(\"PGRST116\")) return null // not found is not exceptional\n  throw e\n}","preventionTips":["Use .maybeSingle() and return null for optional lookups instead of .single() throwing","Validate route params as uuids before fetching","Expect RLS to hide other users' presets — design UI for 'not found'"],"tags":["supabase","postgrest","not-found","single","rls"],"backgroundTag":"supabase-row-not-found","analyzedSha":"81328b61d2a4ab597a7a057be70e785cf756d9f8","analyzedAt":"2026-08-27T19:24:42.689Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}