{"record":{"id":"6ab130bf0a2bf79b","repo":"different-ai/openwork","slug":"member-not-found-error-invalid-desktop-policy-me","errorCode":null,"errorMessage":"member_not_found (Error; invalid desktop policy member IDs)","messagePattern":"member_not_found \\(Error; invalid desktop policy member IDs\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-api/src/routes/org/desktop-policies.ts","lineNumber":87,"sourceCode":"\nfunction resolveRoles(values: DesktopPolicyAssignmentRole[]) {\n  return [...new Set(values)]\n}\n\nasync function resolveMemberIds(input: {\n  organizationId: typeof DesktopPolicyTable.$inferSelect.organizationId\n  values: string[]\n}) {\n  const memberIds = [...new Set(input.values)].map(parseMemberId)\n  if (memberIds.length === 0) return [] as MemberId[]\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 new Error(\"member_not_found\")\n  }\n\n  return memberIds\n}\n\nasync function resolveTeamIds(input: {\n  organizationId: typeof DesktopPolicyTable.$inferSelect.organizationId\n  values: string[]\n}) {\n  const teamIds = [...new Set(input.values)].map(parseTeamId)\n  if (teamIds.length === 0) return [] as TeamId[]\n\n  const rows = await db\n    .select({ id: TeamTable.id })\n    .from(TeamTable)\n    .where(and(eq(TeamTable.organizationId, input.organizationId), inArray(TeamTable.id, teamIds)))\n\n  if (rows.length !== teamIds.length) {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/routes/org/desktop-policies.ts#L69-L105","documentation":"resolveMemberIds validates that every member ID supplied for a desktop policy exists in the given organization and has not been removed. It queries MemberTable filtered by organizationId, the supplied IDs, and removedAt IS NULL; if the matched row count differs from the input count, it throws Error(\"member_not_found\").","triggerScenarios":"Creating/updating an org desktop policy with memberIds containing an ID from another organization, a deleted/nonexistent member, or a member whose removedAt is set (removed from the org).","commonSituations":"Client UI caching member IDs after a member was removed; copying policy JSON between orgs; a member deactivated between fetching the form and submitting; typos or stale test fixtures in API calls.","solutions":["Before saving, verify each member ID via GET org members and drop IDs that no longer resolve.","Refresh the member picker data so removed members are excluded from the submission.","If a member was re-added, use their NEW member ID — the old MemberTable row is tombstoned with removedAt.","Confirm the IDs belong to the same organizationId as the policy being written."],"exampleFix":"// before\nmemberIds: [\"mem_1\", \"mem_removed\"] // throws member_not_found\n// after\nconst valid = await listOrgMembers(orgId)\nmemberIds: [\"mem_1\", ...].filter(id => valid.some(m => m.id === id && !m.removedAt))","handlingStrategy":"validation","validationCode":"const ids = payload.memberIds\nconst rows = await db.select({ id: MemberTable.id }).from(MemberTable)\n  .where(and(eq(MemberTable.organizationId, orgId), inArray(MemberTable.id, ids), isNull(MemberTable.removedAt)))\nif (rows.length !== ids.length) {\n  throw new Error(`Unknown/removed member IDs: ${ids.filter(i => !rows.some(r => r.id === i)).join(\", \")}`)\n}","typeGuard":"function allMembersActive(ids: string[], members: Array<{ id: string; removedAt: Date | null }>): boolean {\n  return ids.every(id => members.some(m => m.id === id && m.removedAt === null))\n}","tryCatchPattern":"try {\n  await saveDesktopPolicy(orgId, { memberIds, ...rest })\n} catch (e) {\n  if (e instanceof Error && e.message === \"member_not_found\") {\n    const fresh = await listOrgMembers(orgId)\n    return saveDesktopPolicy(orgId, { memberIds: memberIds.filter(id => fresh.some(m => m.id === id)), ...rest })\n  }\n  throw e\n}","preventionTips":["Refresh member lists in the UI before submitting policy changes","Drop removed members (removedAt != null) from stored policy drafts","Scope all member ID lookups to the policy's organizationId","Use the new member ID if a removed member is re-added"],"tags":["validation","policy","organization","members"],"backgroundTag":"referenced-entity-not-found","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}