langfuse/langfuse · error · InvalidRequestError

Invalid JSON in filter parameter

Error message

Invalid JSON in filter parameter

What it means

Thrown by the scores API filter parameter parser when the filter query-string value is present but is not valid JSON. The string is JSON.parse'd inside a zod transform; a parse failure (other than a re-thrown InvalidRequestError) is converted to this message.

Source

Thrown at packages/shared/src/features/scores/interfaces/api/shared.ts:69

    .transform((str) => str.split(",").map((id) => id.trim())) // Split the comma-separated string
    .refine((arr) => arr.every((id) => typeof id === "string"), {
      message: "Each score ID must be a string",
    })
    .nullish(),
  fields: commaSeparatedEnumArray(SCORE_FIELD_GROUPS, null, {
    unknownValues: "filter",
  }),
  filter: z
    .string()
    .optional()
    .transform((str) => {
      if (!str) return undefined;
      try {
        const parsed = JSON.parse(str);
        return parsed;
      } catch (e) {
        if (e instanceof InvalidRequestError) throw e;
        throw new InvalidRequestError("Invalid JSON in filter parameter");
      }
    })
    .pipe(z.array(singleFilter).optional()),
});

// POST /scores
// Roughly mirrors ScoreBody in `packages/shared/src/server/ingestion/types.ts`;
// keep them in sync for fields that cross both surfaces.
export const PostScoresBody = applyScoreValidation(
  PostScoreBodyFoundationSchema.extend({
    source: PublicApiCreateScoreSourceDomain.default(ScoreSourceEnum.API),
  }).and(
    z.discriminatedUnion("dataType", [
      z.object({
        value: z.number(),
        dataType: z.literal("NUMERIC"),
        configId: z.string().nullish(),
      }),

View on GitHub (pinned to 59d92c7cf3)

Solutions

  1. URL-encode a proper JSON array for filter, e.g. filter=%5B%7B%22type%22...%5D
  2. Validate/craft the JSON with JSON.stringify on the client rather than by hand
  3. Omit the filter parameter entirely if no filtering is needed (undefined is allowed)

Example fix

// before
GET /api/public/scores?filter=environment=production
// after
GET /api/public/scores?filter=%5B%7B%22column%22%3A%22environment%22%2C%22type%22%3A%22stringOptions%22%2C%22operator%22%3A%22%3D%22%2C%22value%22%3A%22production%22%7D%5D
Defensive patterns

Strategy: validation

Validate before calling

let filter: unknown;
if (rawFilterParam) { try { filter = JSON.parse(rawFilterParam); } catch { return badRequest('filter must be a JSON array'); } }

Type guard

const isValidFilterJson = (s: string) => { try { const v = JSON.parse(s); return Array.isArray(v); } catch { return false; } };

Try / catch

catch (e) { if (e instanceof InvalidRequestError && e.message === 'Invalid JSON in filter parameter') { /* re-encode filter with encodeURIComponent(JSON.stringify(arr)) */ } throw e; }

Prevention

When it happens

Trigger: GET /api/public/scores?filter=environment%3Dproduction or any non-JSON filter string like filter=abc instead of filter=[{"type":"stringOptions",...}].

Common situations: Passing key=value style filters instead of a JSON array; broken URL encoding of quotes/brackets; trailing commas in hand-written JSON.

Understand the failure class

Related errors


AI-assisted analysis of langfuse/langfuse@59d92c7cf3 (2026-08-27). Data as JSON: /api/errors/283dee904a75fa9c. Report an issue: GitHub.