{"record":{"id":"a76527f8f38e042b","repo":"actualbudget/actual","slug":"path-does-not-exist","errorCode":null,"errorMessage":"Path does not exist: ","messagePattern":"Path does not exist: ","errorType":"exception","errorClass":"CompileError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/aql/compiler.ts","lineNumber":105,"sourceCode":"\n    if (!table[field] || table[field].ref == null) {\n      throw new CompileError(\n        `Field not joinable on table ${tableName}: \"${field}\"`,\n      );\n    }\n\n    return table[field].ref;\n  }, initialTable);\n\n  let joinTable;\n  const parentParts = parts.slice(0, -1);\n  if (parentParts.length === 1) {\n    joinTable = parentParts[0];\n  } else {\n    const parentPath = parentParts.join('.');\n    const parentDesc = paths.get(parentPath);\n    if (!parentDesc) {\n      throw new CompileError('Path does not exist: ' + parentPath);\n    }\n    joinTable = parentDesc.tableId;\n  }\n\n  return {\n    tableName,\n    tableId: uid(tableName),\n    joinField: parts[parts.length - 1],\n    joinTable,\n  };\n}\n\nfunction resolvePath(state, path) {\n  let paths = path.split('.');\n\n  paths = paths.reduce(\n    (acc, name) => {\n      const fullName = acc.context + '.' + name;","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/aql/compiler.ts#L87-L123","documentation":"For deeply nested paths (3+ segments), makePath (compiler.ts:104) requires that the parent path was already resolved and registered in state.paths. If the parent path (all segments except the last) is missing from the paths map, this CompileError is thrown — joins must be established outer-to-inner.","triggerScenarios":"Requesting a nested path whose parent was never resolved first, e.g. building path info directly via makePath for 'a.b.c' when 'a.b' was never put through resolvePath/makePath; in practice triggered by manually constructing nested field references out of order.","commonSituations":"Custom tooling calling internal compiler functions directly; constructing query state programmatically and skipping intermediate path registration; unusual nested relationships referenced before their parents in the same expression.","solutions":["Resolve the full path through normal query APIs (q.select/q.filter with the dotted string) so parents are registered automatically via resolvePath.","If calling internals, register parent paths in state.paths before the child path.","Simplify overly deep paths — Actual's schema rarely needs more than two segments; verify the relationship exists."],"exampleFix":"// before (manual, out of order)\nmakePath(state, 'transactions.category.group.id');\n// after\nresolvePath(state, 'transactions.category');\nmakePath(state, 'transactions.category.group');","handlingStrategy":"validation","validationCode":"function assertParentsResolved(state, path) {\n  const parts = path.split('.');\n  for (let i = 1; i < parts.length; i++) {\n    const parent = parts.slice(0, i).join('.');\n    if (i > 1 && !state.paths.get(parent)) {\n      throw new Error(`Parent path not resolved: ${parent}`);\n    }\n  }\n}","typeGuard":"null","tryCatchPattern":"try {\n  makePath(state, deepPath);\n} catch (e) {\n  if (e.message.startsWith('Path does not exist:')) {\n    // resolve parent paths first via resolvePath, then retry once\n    resolvePath(state, deepPath.split('.').slice(0, -1).join('.'));\n  } else throw e;\n}","preventionTips":["Prefer public query-builder APIs which resolve paths in order","If using internals, always resolve outer segments before inner ones","Avoid paths deeper than two segments unless the schema supports it"],"tags":["aql","query","path","compile-error"],"backgroundTag":"path-not-resolved","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}