langfuse/langfuse · error · InvalidRequestError
Invalid relationTable: ${relationTableName}. Must be one of
Error message
Invalid relationTable: ${relationTableName}. Must be one of ${Object.keys(view.tableRelations)} What it means
Thrown by buildJoins when a query requests a relation table that is not registered in view.tableRelations for the current entity. The join layer only knows a fixed set of relation tables per view.
Source
Thrown at packages/shared/src/features/query/server/queryBuilder.ts:803
// Replace each @@AGGN@@ placeholder with its corresponding value
for (const [placeholder, replacement] of Object.entries(aggs)) {
const marker = `@@${placeholder.toUpperCase()}@@`;
result = result.replaceAll(marker, replacement);
}
return result;
}
private buildJoins(
relationTables: Set<string>,
view: ViewDeclarationType,
filterList: FilterList,
query: QueryType,
skipObservationsFinal: boolean,
) {
const relationJoins = [];
for (const relationTableName of relationTables) {
if (!(relationTableName in view.tableRelations)) {
throw new InvalidRequestError(
`Invalid relationTable: ${relationTableName}. Must be one of ${Object.keys(view.tableRelations)}`,
);
}
const relation = view.tableRelations[relationTableName];
// Conditionally add FINAL - skip for observations if flag is set, and respect per-relation useFinal
const shouldUseFinal =
(relation.useFinal ?? true) &&
!(relation.name === "observations" && skipObservationsFinal);
const alias =
relation.name !== relationTableName ? ` AS ${relationTableName}` : "";
let joinStatement = `INNER JOIN ${relation.name}${alias}${shouldUseFinal ? " FINAL" : ""} ${relation.joinConditionSql}`;
// Create time dimension mapping for the relation table
const relationTimeDimensionMapping = {
uiTableName: relation.timeDimension,
uiTableId: relation.timeDimension,
clickhouseTableName: relation.name,View on GitHub (pinned to 59d92c7cf3)
Solutions
- Use only relation names listed in the error (Object.keys(view.tableRelations))
- Construct queries via the provided helper functions rather than assembling relationTables manually
Defensive patterns
Strategy: validation
Validate before calling
const valid = new Set(Object.keys(view.tableRelations)); const safe = relationTables.filter(r => valid.has(r));
Type guard
const isValidRelationTable = (r: string, view: QueryView) => r in view.tableRelations;
Prevention
- Use QueryBuilder helper constructors instead of hand-assembling relationTables
- Keep relation names in a shared constant list
When it happens
Trigger: Passing relationTables containing an unknown table name (e.g. "scores" on a view whose tableRelations lacks it) into QueryBuilder.build.
Common situations: Hand-constructing QueryBuilder options instead of using helper constructors; version skew where a relation exists in one view but not another; typos in relation names.
Related errors
- Field '${filter.column}' cannot be used as a filter.
- Invalid query dimension '${filter.column}': Unsupported dime
- Invalid filter for field '${filter.column}': Filter type '${
- Invalid filter for field '${filter.column}': arrayOptions ty
- Invalid filter for field '${filter.column}': Filter type '${
AI-assisted analysis of langfuse/langfuse@59d92c7cf3 (2026-08-27).
Data as JSON: /api/errors/b8942582e4c45009.
Report an issue: GitHub.