{"record":{"id":"06a866ad5559013a","repo":"actualbudget/actual","slug":"parameter-param-paramname-can-t-convert-to","errorCode":null,"errorMessage":"Parameter \"${param.paramName}\" can't convert to ${type} (already inferred as ${existingType})","messagePattern":"Parameter \"(.+?)\" can't convert to (.+?) \\(already inferred as (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/aql/compiler.ts","lineNumber":237,"sourceCode":"  throw new CompileError(`Bad ${type} format: ${str}`);\n}\n\nfunction inferParam(param, type) {\n  const existingType = param.paramType;\n  if (existingType) {\n    const casts = {\n      date: ['string'],\n      'date-month': ['date'],\n      'date-year': ['date', 'date-month'],\n      id: ['string'],\n      float: ['integer'],\n    };\n\n    if (\n      existingType !== type &&\n      (!casts[type] || !casts[type].includes(existingType))\n    ) {\n      throw new Error(\n        `Parameter \"${param.paramName}\" can't convert to ${type} (already inferred as ${existingType})`,\n      );\n    }\n  } else {\n    param.paramType = type;\n  }\n}\n\nfunction castInput(state, expr, type) {\n  if (expr.type === type) {\n    return expr;\n  } else if (expr.type === 'param') {\n    inferParam(expr, type);\n    return typed(expr.value, type);\n  } else if (expr.type === 'null') {\n    if (!expr.literal) {\n      throw new CompileError(\"A non-literal null doesn't make sense\");\n    }","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/aql/compiler.ts#L219-L255","documentation":"AQL parameters are typed on first use via inferParam (compiler.ts:222). If the same named parameter is later used where an incompatible type is required (and no automatic cast exists in the casts table, e.g. string->date, date->date-month, integer->float), a plain Error is thrown because the parameter would need contradictory SQL types.","triggerScenarios":"Reusing one parameter in contexts demanding different types: q('transactions').filter({ date: { $eq: ':param' } }).filter({ amount: { $gt: ':param' } }) — first inferred as date, then required as float.","commonSituations":"Reusing a single placeholder across filters of different columns; generating queries in loops where one param name is shared; building generic query helpers that always use the same param key.","solutions":["Use a distinct parameter name per logical type, e.g. :startDate for dates and :minAmount for numbers.","If reuse is intended, pass values whose types are compatible with the casts table (string can become date/id; integer can become float; date can become date-month/date-year).","Ensure the first usage of the parameter is the type you intend, since the first context fixes paramType."],"exampleFix":"// before\n.filter({ date: { $gte: ':p' } }).filter({ amount: { $gt: ':p' } })\n// after\n.filter({ date: { $gte: ':startDate' } }).filter({ amount: { $gt: ':minAmount' } })","handlingStrategy":"validation","validationCode":"function makeParams(spec) {\n  // spec: { startDate: 'date', minAmount: 'float' }\n  const used = new Map();\n  for (const [name, type] of Object.entries(spec)) {\n    if (used.has(name) && used.get(name) !== type) {\n      throw new Error(`Param ${name} used with conflicting types`);\n    }\n    used.set(name, type);\n  }\n  return spec;\n}","typeGuard":"function paramTypeMatches(param, type) {\n  const casts = { date: ['string'], id: ['string'], float: ['integer'], 'date-month': ['date'], 'date-year': ['date', 'date-month'] };\n  return param.paramType === type || (casts[type] || []).includes(param.paramType);\n}","tryCatchPattern":"try {\n  runQuery(q, params);\n} catch (e) {\n  if (e.message.includes(\"can't convert to\")) {\n    throw new Error('Use a distinct parameter name for each value type');\n  } else throw e;\n}","preventionTips":["Name parameters after their type/purpose (:startDate, :minAmount)","Never reuse one placeholder across differently-typed expressions","Document param types in query-builder helper functions"],"tags":["aql","parameters","types","compile-error"],"backgroundTag":"parameter-type-conflict","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}