{"record":{"id":"3d9da1383f6ef3d0","repo":"different-ai/openwork","slug":"member-not-found-3d9da1","errorCode":"member_not_found","errorMessage":"member_not_found","messagePattern":"member_not_found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"ee/apps/den-api/src/routes/org/llm-providers.ts","lineNumber":300,"sourceCode":"\nfunction parseTeamId(value: string) {\n  return normalizeDenTypeId(\"team\", value)\n}\n\nasync function resolveMemberIds(input: {\n  organizationId: typeof LlmProviderTable.$inferSelect.organizationId\n  values: string[]\n}) {\n  const uniqueValues = [...new Set(input.values)]\n  if (uniqueValues.length === 0) {\n    return [] as MemberId[]\n  }\n\n  const memberIds = uniqueValues.map((value) => {\n    try {\n      return parseMemberId(value)\n    } catch {\n      throw createFailure(404, \"member_not_found\")\n    }\n  })\n\n  const rows = await db\n    .select({ id: MemberTable.id })\n    .from(MemberTable)\n    .where(and(eq(MemberTable.organizationId, input.organizationId), inArray(MemberTable.id, memberIds), isNull(MemberTable.removedAt)))\n\n  if (rows.length !== memberIds.length) {\n    throw createFailure(404, \"member_not_found\")\n  }\n\n  return memberIds\n}\n\nasync function resolveTeamIds(input: {\n  organizationId: typeof LlmProviderTable.$inferSelect.organizationId\n  values: string[]","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/routes/org/llm-providers.ts#L282-L318","documentation":"resolveMemberIds validates each supplied member identifier with parseMemberId. If any value is not a well-formed member ID, it throws a 404 failure with code member_not_found before ever querying the database. This is a format-level rejection of a member reference in an LLM provider grant/credential payload.","triggerScenarios":"An org LLM provider create/update request includes a member ID string that fails parseMemberId (malformed ID — wrong prefix, not a UUID, empty string).","commonSituations":"Client sends a user email, slug, or truncated ID instead of the canonical member ID; IDs copied from a different table (user id vs member id); stale hardcoded IDs after a migration.","solutions":["Inspect the request payload and replace the malformed value with a valid member table ID.","Fetch valid member IDs via the org members list endpoint rather than constructing them client-side.","Confirm you are sending member IDs (MemberTable.id), not user IDs.","If the ID was stored previously, re-resolve it — members can be removed and re-added with new IDs."],"exampleFix":"// before\n{ \"memberIds\": [\"alice@example.com\"] }\n// after\n{ \"memberIds\": [\"mem_9f2c1a7e-4b3d-4e2a-8f1b-2c9d5e6a7b8c\"] }","handlingStrategy":"validation","validationCode":"const MEMBER_ID_RE = /^(mem_|m_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nif (!values.every(v => MEMBER_ID_RE.test(v))) throw new Error('malformed member id');","typeGuard":"function isMemberId(v: unknown): v is string { return typeof v === 'string' && v.length > 0 && isUuidLike(v) }","tryCatchPattern":"try { await updateProviderGrants({ memberIds }) } catch (e) { if (e.code === 'member_not_found') await refreshMemberCache(); }","preventionTips":["Always source member IDs from the org members list endpoint","Never send emails, slugs, or user IDs where member IDs are expected","Cache member IDs with invalidation on membership changes"],"tags":["http-404","validation","org-members"],"backgroundTag":"resource-not-found","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}