{"record":{"id":"33a9443df55d8f09","repo":"actualbudget/actual","slug":"invalid-id-must-be-string","errorCode":null,"errorMessage":"Invalid id, must be string: ","messagePattern":"Invalid id, must be string: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/aql/schema-helpers.ts","lineNumber":42,"sourceCode":"      if (value instanceof Date) {\n        return toDateRepr(dayFromDate(value));\n      } else if (\n        value.match(/^\\d{4}-\\d{2}-\\d{2}$/) == null ||\n        value < '1995-01-01'\n      ) {\n        throw new Error('Invalid date: ' + value);\n      }\n\n      return toDateRepr(value);\n    case 'date-month':\n      return toDateRepr(value.slice(0, 7));\n    case 'date-year':\n      return toDateRepr(value.slice(0, 4));\n    case 'boolean':\n      return value ? 1 : 0;\n    case 'id':\n      if (typeof value !== 'string' && value !== null) {\n        throw new Error('Invalid id, must be string: ' + value);\n      }\n      return value;\n    case 'integer':\n      if (typeof value === 'number' && Number.isInteger(value)) {\n        return value;\n      } else {\n        throw new Error(\"Can't convert to integer: \" + JSON.stringify(value));\n      }\n    case 'json':\n      return JSON.stringify(value);\n    default:\n  }\n  return value;\n}\n\nexport function convertOutputType(value, type) {\n  if (value === null) {\n    if (type === 'boolean') {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/aql/schema-helpers.ts#L24-L60","documentation":"Values cast to the `id` type must be a string (or explicitly null). Ids in Actual are opaque strings; passing a number, object, or other non-string type indicates a bug and is rejected with 'Invalid id, must be string'.","triggerScenarios":"Passing a numeric id (e.g. from a CSV import or external system), an auto-increment integer, or an object/undefined that is not null into an `id`-typed query field such as account, payee, or transaction id.","commonSituations":"Importing data from other finance tools that use integer keys, comparing ids parsed from JSON where numbers leaked in, or using a library/entity record where the id field was never converted to Actual's string id format.","solutions":["Convert the value to a string before querying: `String(id)` — but only if it is a legitimate id.","Generate proper Actual ids with the id utilities (e.g. `.randomUUID()` based ids) instead of external numeric keys.","If the id may legitimately be absent, pass null explicitly rather than 0 or a number.","Trace where the non-string id originates (importer, API response) and fix the mapping layer."],"exampleFix":"// before\nq('transactions').filter({ account: row.accountId }); // number 42\n// after\nq('transactions').filter({ account: row.accountId != null ? String(row.accountId) : null });","handlingStrategy":"type-guard","validationCode":"const isValidId = (v) => typeof v === 'string' && v.length > 0;\nif (!isValidId(id)) throw new Error('caller must supply a string id');","typeGuard":"const isStringId = (v) => typeof v === 'string';","tryCatchPattern":"try {\n  return await runQuery(q.filter({ account: id }));\n} catch (e) {\n  if (e.message.startsWith('Invalid id, must be string:')) {\n    logger.error('Non-string id passed to query', { id });\n    return null;\n  }\n  throw e;\n}","preventionTips":["Never pass numeric keys from external systems directly as ids","Generate ids with Actual's id utilities","Pass explicit null (not 0/undefined) when an id is intentionally absent"],"tags":["id","types","validation","aql"],"backgroundTag":"invalid-id-type","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}