{"record":{"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":"exception","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/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/time-series/lib/commands/helpers.ts#L320-L356","documentation":"parseQueryLabelsFilterArgument throws when a caller passes an empty array for the FILTER argument of TS.QUERYLABELS / TS.QUERYLABELS_VALUES. The library treats filter=[] as a local usage error rather than silently widening the query to all indexed series — the server also rejects a bare FILTER token with no expressions. To query every indexed series, omit the filter argument entirely. Expressions are sent verbatim (not parsed, reordered, or deduplicated).","triggerScenarios":"Calling client.queryLabels({ filter: [] }) or client.queryLabelsValues({ filter: [] }); or building the filter list dynamically such that it ends up empty (e.g., a label-matching step returned no expressions).","commonSituations":"A filter-expression builder returns [] when no labels match a condition; copy-paste from another command where an empty array was tolerated; assuming [] means 'no filter' (it does not — undefined does).","solutions":["Omit the filter argument entirely to query all indexed series: client.queryLabels() with no filter.","If you compute the list dynamically, fall back to undefined when it is empty: filter: list.length ? list : undefined.","If you intend to filter, ensure at least one expression such as 'label=value' is present before calling."],"exampleFix":"// before — empty array is a usage error\nawait client.queryLabels({ filter: expressions }); // expressions === []\n\n// after — omit filter when empty to query all series\nawait client.queryLabels({\n  filter: expressions.length ? expressions : undefined\n});","handlingStrategy":"type-guard","validationCode":"import type { RedisVariadicArgument } from '@redis/client';\nfunction normalizeFilter(filter?: RedisVariadicArgument): RedisVariadicArgument | undefined {\n  if (filter === undefined) return undefined;\n  const arr = Array.isArray(filter) ? filter : [filter];\n  return arr.length > 0 ? filter : undefined; // empty -> omit to query all series\n}\n// usage:\nawait client.queryLabels({ filter: normalizeFilter(expressions) });","typeGuard":"const isNonEmptyFilter = (f: unknown): f is string | [string, ...string[]] =>\n  typeof f === 'string' || (Array.isArray(f) && f.length > 0 && f.every(x => typeof x === 'string'));","tryCatchPattern":null,"preventionTips":["Remember the contract: omit filter to query all series; never pass [].","When building filter lists dynamically, coerce [] to undefined at the call site.","Add a type guard so empty arrays never reach parseQueryLabelsFilterArgument.","Document on the command's JSDoc that filter is optional and must be non-empty if provided."],"tags":["time-series","querylabels","validation","typescript"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}