{"id":"8c6c29d8766f59f4","repo":"redis/node-redis","slug":"filter-was-given-an-empty-filter-expression-list","errorCode":null,"errorMessage":"FILTER was given an empty filter expression list; omit it to query all indexed series","messagePattern":"FILTER was given an empty filter expression list; omit it to query all indexed series","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/time-series/lib/commands/helpers.ts","lineNumber":338,"sourceCode":"  parser.push('SELECTED_LABELS');\n  parser.pushVariadic(selectedLabels);\n}\n\n/**\n * Pushes the optional `FILTER filterExpr [filterExpr ...]` tail shared by the\n * `TS.QUERYLABELS` forms. Omitting `filter` queries all indexed series; passing\n * an explicitly empty array is a local usage error rather than a silent widen to\n * all series (the server also rejects a bare `FILTER` token). Expressions are\n * sent verbatim — not parsed, reordered, or deduplicated.\n */\nexport function parseQueryLabelsFilterArgument(\n  parser: CommandParser,\n  filter?: RedisVariadicArgument\n) {\n  if (filter === undefined) return;\n\n  if (Array.isArray(filter) && filter.length === 0) {\n    throw new Error('FILTER was given an empty filter expression list; omit it to query all indexed series');\n  }\n\n  parser.push('FILTER');\n  parser.pushVariadic(filter);\n}\n\nexport type RawLabelValue = BlobStringReply | NullReply;\n\nexport type RawLabels<T extends RawLabelValue> = ArrayReply<TuplesReply<[\n  label: BlobStringReply,\n  value: T\n]>>;\n\nexport function transformRESP2Labels<T extends RawLabelValue>(\n  labels: RawLabels<T>,\n  typeMapping?: TypeMapping\n): MapReply<BlobStringReply, T> {\n  const unwrappedLabels = labels as unknown as UnwrapReply<typeof labels>;","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/time-series/lib/commands/helpers.ts#L320-L356","documentation":"Thrown by parseQueryLabelsFilterArgument() (helpers.ts:337) when the caller passes an explicitly empty array as the FILTER list to TS.QUERYLABELS or TS.QUERYLABELS_VALUES. Passing `filter: []` would otherwise widen the command to query all series, which the server also rejects (a bare FILTER token with no expressions is invalid), so the client treats it as a local usage error and tells you to omit the argument entirely to query all indexed series.","triggerScenarios":"Calling client.ts.queryLabels({ filter: [] }) or queryLabelsValues({ filter: [] }) — passing an array that is non-undefined but empty.","commonSituations":"Dynamically building a filter list from user input that happened to be empty; defaulting an optional filter param to [] instead of undefined; programmatically concatenating filters and ending up with none.","solutions":["Omit the filter argument entirely to query all indexed series.","If your filter list is dynamic, pass `filter.length ? filter : undefined`.","Guard the call site so an empty list is never passed: `if (!filters.length) return client.ts.queryLabels();`.","Ensure at least one filter expression (e.g. 'name=temperature') when you intend to narrow."],"exampleFix":"// before\nawait client.ts.queryLabels({ filter: [] });\n// after — query all series\nawait client.ts.queryLabels();\n// or, dynamic\nawait client.ts.queryLabels({ filter: filters.length ? filters : undefined });","handlingStrategy":"validation","validationCode":"// Normalize a dynamic filter list before calling TS.QUERYLABELS / QUERYLABELS_VALUES.\nfunction normalizeFilter(filter) {\n  if (filter === undefined) return undefined;\n  if (Array.isArray(filter)) {\n    if (filter.length === 0) return undefined; // query all series\n    return filter;\n  }\n  return filter; // single expression string\n}\nawait client.ts.queryLabels({ filter: normalizeFilter(filters) });","typeGuard":"function isNonEmptyFilterList(f: unknown): f is string[] | string {\n  if (typeof f === 'string') return f.length > 0;\n  if (Array.isArray(f)) return f.length > 0 && f.every(x => typeof x === 'string');\n  return false;\n}","tryCatchPattern":"try {\n  await client.ts.queryLabels({ filter });\n} catch (e) {\n  if (e instanceof Error && /empty filter expression list/.test(e.message)) {\n    await client.ts.queryLabels(); // retry querying all series\n  } else throw e;\n}","preventionTips":["Default optional filter params to undefined, never [].","At call sites that build filters from user input, pass `filters.length ? filters : undefined`.","Add a unit test asserting queryLabels() is never invoked with an empty array.","Treat 'no filter' as 'omit the argument', matching the documented behavior."],"tags":["time-series","usage","validation","querylabels"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}