{"record":{"id":"71dccfb465f35669","repo":"actualbudget/actual","slug":"new-owner-not-found","errorCode":null,"errorMessage":"New owner not found","messagePattern":"New owner not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/sync-server/src/services/user-service.ts","lineNumber":117,"sourceCode":"export function deleteUserAccess(userId) {\n  try {\n    return getAccountDb().mutate('DELETE FROM user_access WHERE user_id = ?', [\n      userId,\n    ]).changes;\n  } catch (error) {\n    throw new Error(`Failed to delete user access: ${error.message}`);\n  }\n}\n\nexport function transferAllFilesFromUser(ownerId, oldUserId) {\n  if (!ownerId || !oldUserId) {\n    throw new Error('Invalid user IDs');\n  }\n  try {\n    getAccountDb().transaction(() => {\n      const ownerExists = getUserById(ownerId);\n      if (!ownerExists) {\n        throw new Error('New owner not found');\n      }\n      getAccountDb().mutate('UPDATE files set owner = ? WHERE owner = ?', [\n        ownerId,\n        oldUserId,\n      ]);\n    });\n  } catch (error) {\n    throw new Error(`Failed to transfer files: ${error.message}`);\n  }\n}\n\nexport function updateFileOwner(ownerId, fileId) {\n  if (!ownerId || !fileId) {\n    throw new Error('Invalid parameters');\n  }\n  try {\n    const result = getAccountDb().mutate(\n      'UPDATE files set owner = ? WHERE id = ?',","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/services/user-service.ts#L99-L135","documentation":"transferAllFilesFromUser wraps all file-reassignment work in a database transaction and first verifies the target owner exists via getUserById. If no user row matches the supplied ownerId, it throws 'New owner not found' to abort the transfer before any files are reassigned. The transaction rolls back, so no partial ownership changes occur.","triggerScenarios":"Calling transferAllFilesFromUser(ownerId, oldUserId) with an ownerId that has no row in the users table — e.g. a deleted or never-created user id, a stale id from a different database, or a caller (like loginWithOpenIdFinalize) resolving the owner from a token/sub claim that was not yet inserted as a user.","commonSituations":"OpenID login flows where the identity provider's subject does not match any local user record; passing a userName instead of a user id; referencing a user deleted by another admin between fetching and transferring; pointing at the wrong account.sqlite database.","solutions":["Verify the ownerId exists before calling: run getUserById(ownerId) and create/insert the user if null","Check you are passing the user's id (users.id), not the userName or displayName","Confirm the sync server is pointed at the same account database that contains the owner user (check the correct server/data dir)","If the owner was deleted, recreate the user or pick a different existing owner id"],"exampleFix":"// before\nawait transferAllFilesFromUser(subFromToken, oldUserId);\n// after\nconst ownerId = getUserByUsername(subFromToken);\nif (!ownerId) throw new Error(`Owner ${subFromToken} not provisioned yet`);\nawait transferAllFilesFromUser(ownerId, oldUserId);","handlingStrategy":"validation","validationCode":"import { getUserById } from './services/user-service';\nfunction assertOwnerExists(ownerId) {\n  if (!getUserById(ownerId)) {\n    throw new Error(`Cannot transfer: owner ${ownerId} does not exist`);\n  }\n}\nassertOwnerExists(ownerId);","typeGuard":"function isExistingUser(userId) {\n  return typeof userId === 'string' && userId.length > 0 && getUserById(userId) !== null;\n}","tryCatchPattern":"try {\n  transferAllFilesFromUser(ownerId, oldUserId);\n} catch (e) {\n  if (e.message.includes('New owner not found')) {\n    logger.error(`Owner ${ownerId} missing; provision it before transferring`);\n  }\n  throw e;\n}","preventionTips":["Provision the OpenID user (insert into users) before any file transfer in login finalization","Always pass users.id, never userName or display name","Look up the owner with getUserById immediately before transferring to avoid TOCTOU deletions","Confirm the server points at the intended account.sqlite"],"tags":["sync-server","openid","user-not-found","database"],"backgroundTag":"entity-not-found","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}