{"record":{"id":"a5ef573e030caadb","repo":"remix-run/remix","slug":"hasmanythrough-expects-a-through-relation-whose-so","errorCode":null,"errorMessage":"hasManyThrough expects a through relation whose source table matches {getTableName(source)}","messagePattern":"hasManyThrough expects a through relation whose source table matches (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/data-table/src/lib/table.ts","lineNumber":937,"sourceCode":"  })\n}\n\n/**\n * Defines a one-to-many relation from `source` to `target` through an intermediate relation.\n * @param source Source table.\n * @param target Target table.\n * @param relationOptions Through relation configuration.\n * @returns A relation descriptor.\n */\nexport function hasManyThrough<source extends AnyTable, target extends AnyTable>(\n  source: source,\n  target: target,\n  relationOptions: HasManyThroughOptions<source, target>,\n): Relation<source, target, 'many'> {\n  let throughRelation = relationOptions.through\n\n  if (throughRelation.sourceTable !== source) {\n    throw new Error(\n      'hasManyThrough expects a through relation whose source table matches ' +\n        getTableName(source),\n    )\n  }\n\n  let throughTargetKey = normalizeKeysForTable(\n    throughRelation.targetTable,\n    relationOptions.throughTargetKey,\n    'throughTargetKey',\n    getTablePrimaryKey(throughRelation.targetTable),\n  )\n  let throughForeignKey = normalizeKeySelector(\n    target,\n    relationOptions.throughForeignKey,\n    'throughForeignKey',\n    [inferForeignKey(getTableName(throughRelation.targetTable))],\n  )\n","sourceCodeStart":919,"sourceCodeEnd":955,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table/src/lib/table.ts#L919-L955","documentation":"hasManyThrough builds a many relation through an intermediate relation; the through relation must itself originate from the same source table. This error fires when throughRelation.sourceTable does not match the table you called hasManyThrough on, which would make the join chain ambiguous.","triggerScenarios":"Calling hasManyThrough(source, target, { through: someRelation }) where someRelation was created with belongsTo/hasMany on a different source table than the one passed as source.","commonSituations":"Reusing a relation defined on another table (e.g. passing tasks.user when defining an accounts relation), or reordering arguments so source is actually the target.","solutions":["Pass a through relation created from the same source table as the first argument","Double-check the argument order: hasManyThrough(source, target, { through })","Define the through relation inline with belongsTo(source, ...) so the source table always matches"],"exampleFix":"// before\nconst accountTasks = (table: Tables) =>\n  hasManyThrough(accounts, tasks, { through: relations.user /* defined on tasks */ })\n// after\nconst accountTasks = (table: Tables) =>\n  hasManyThrough(accounts, tasks, { through: accountUsers /* belongsTo(accounts, users) */ })","handlingStrategy":"validation","validationCode":"if (throughRelation.sourceTable !== source) throw new Error('through relation source mismatch')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Define through relations inline from the source table","Keep relation factories per-table so sources cannot be mixed up"],"tags":["data-table","relations","join"],"backgroundTag":"invalid-relation-definition","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}