{"record":{"id":"42f1caaf8b67a7de","repo":"n8n-io/n8n","slug":"cannot-create-relation-id-map-for-a-single-value-b","errorCode":null,"errorMessage":"Cannot create relation id map for a single value because relation contains multiple referenced columns.","messagePattern":"Cannot create relation id map for a single value because relation contains multiple referenced columns\\.","errorType":"exception","errorClass":"TypeORMError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/metadata/RelationMetadata.ts","lineNumber":378,"sourceCode":"\t\t// console.log(\"entity\", entity);\n\t\t// console.log(\"referencedColumns\", referencedColumns);\n\t\treturn EntityMetadata.getValueMap(entity, referencedColumns);\n\t}\n\n\t/**\n\t * Ensures that given object is an entity id map.\n\t * If given id is an object then it means its already id map.\n\t * If given id isn't an object then it means its a value of the id column\n\t * and it creates a new id map with this value and name of the primary column.\n\t */\n\tensureRelationIdMap(id: any): ObjectLiteral {\n\t\tif (ObjectUtils.isObject(id)) return id;\n\n\t\tconst joinColumns = this.isOwning ? this.joinColumns : this.inverseRelation!.joinColumns;\n\t\tconst referencedColumns = joinColumns.map((joinColumn) => joinColumn.referencedColumn!);\n\n\t\tif (referencedColumns.length > 1)\n\t\t\tthrow new TypeORMError(\n\t\t\t\t`Cannot create relation id map for a single value because relation contains multiple referenced columns.`,\n\t\t\t);\n\n\t\treturn referencedColumns[0].createValueMap(id);\n\t}\n\n\t/**\n\t * Extracts column value from the given entity.\n\t * If column is in embedded (or recursive embedded) it extracts its value from there.\n\t */\n\tgetEntityValue(\n\t\tentity: ObjectLiteral,\n\t\tgetLazyRelationsPromiseValue: boolean = false,\n\t): any | undefined {\n\t\tif (entity === null || entity === undefined) return undefined;\n\t\t// extract column value from embeddeds of entity if column is in embedded\n\t\tif (this.embeddedMetadata) {\n\t\t\t// example: post[data][information][counters].id where \"data\", \"information\" and \"counters\" are embeddeds","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/metadata/RelationMetadata.ts#L360-L396","documentation":"Thrown by RelationMetadata.ensureRelationIdMap when a scalar id is passed for a relation whose join columns reference more than one column (composite foreign key). The method picks joinColumns (owning side) or inverseRelation.joinColumns, derives referencedColumns, and refuses when that array length exceeds 1 because a single value cannot populate composite FK columns.","triggerScenarios":"Calling a relation-id helper or repository method that funnels a raw scalar through ensureRelationIdMap on a relation backed by a composite FK (e.g. @ManyToOne joining on two columns).","commonSituations":"Multi-tenant schemas where relations use composite keys; converting a single-column FK to a composite one without updating callers that pass scalar ids.","solutions":["Pass an object id-map keyed by the referenced column names instead of a scalar.","If the relation truly has a single FK, audit @JoinColumn arrays and ensure only one join column is declared.","Wrap relation lookups behind a helper that always emits the composite id-map shape."],"exampleFix":"// before\nawait postRepo.createQueryBuilder().relation('category').of(5).loadOne();\n// where category uses composite FK\n\n// after\nawait postRepo\n  .createQueryBuilder()\n  .relation('category')\n  .of({ tenantId: 1, id: 5 })\n  .loadOne();","handlingStrategy":"type-guard","validationCode":"function relationHasCompositeFk(\n  metadata: EntityMetadata,\n  relationPath: string,\n): boolean {\n  const rel = metadata.relations.find((r) => r.propertyPath === relationPath);\n  if (!rel) return false;\n  const joinCols = rel.isOwning ? rel.joinColumns : rel.inverseRelation?.joinColumns ?? [];\n  return joinCols.length > 1;\n}\n\n// pass an object id when true\nconst idShape = relationHasCompositeFk(meta, 'category')\n  ? { tenantId, id }\n  : categoryId;","typeGuard":"type ScalarId = string | number;\ntype IdMap = Record<string, unknown>;\nfunction isScalarId(id: unknown): id is ScalarId {\n  return typeof id === 'string' || typeof id === 'number';\n}\nfunction isCompositeFk(relation: RelationMetadata): boolean {\n  const cols = relation.isOwning\n    ? relation.joinColumns\n    : relation.inverseRelation?.joinColumns ?? [];\n  return cols.length > 1;\n}","tryCatchPattern":"try {\n  await postRepo.createQueryBuilder().relation('category').of(id).loadOne();\n} catch (err) {\n  if (err.message.includes('multiple referenced columns')) {\n    // re-issue with an id-map object\n  }\n  throw err;\n}","preventionTips":["Centralize relation-id construction in a helper that inspects joinColumns.length.","Document composite-FK relations explicitly so callers know an object id is required.","Add a unit test asserting the join-column count for each composite-FK relation."],"tags":["composite-key","foreign-key","relation","runtime"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}