Skip to content

Public lookup route builds object-shaped filter rules the data layer refuses: GET /forms/:slug/lookup/:field answers 400 INVALID_FILTER for every search #16581

Description

@claude

Symptom

A FormView with sharing.allowAnonymous: true and a section field { field: 'job', publicPicker: { displayFields: ['title', 'city'], filter: [{ field: 'status', operator: 'equals', value: 'published' }] } } renders on /_console/f/SLUG, but the picker's search endpoint fails for every query:

GET /api/v1/forms/apply/lookup/job?q=engineer
HTTP 400
{"code":"INVALID_FILTER","error":"Malformed $filter: [{\"field\":\"status\",\"operator\":\"equals\",\"value\":\"published\"},{\"field\":\"title\",\"operator\":\"contains\",\"value\":\"engineer\"}] is not a recognised filter shape. A filter array is a comparison [field, operator, value], a logical node [\"and\"|\"or\", ...conditions], or a list of those. ..."}

Measured on @objectstack/rest + @objectstack/cli 17.3.0 (objectstack dev --database-driver memory), anonymous curl, no cookie.

Where

packages/rest/src/rest-server.ts, registerFormEndpoints, the GET {basePath}/forms/:slug/lookup/:field handler:

const filters: any[] = [];
if (Array.isArray(picker.filter)) filters.push(...picker.filter);          // ViewFilterRule objects {field, operator, value}
if (q) filters.push({ field: displayFields[0], operator: 'contains', value: q });  // same object shape
...
await p.findData({ object: referenceTo, query: { limit, offset: 0, filters, select, sort }, context });

query.filters reaches the ObjectQL filter parser as $filter, and the parser accepts only comparison TRIPLES [field, operator, value] / logical nodes. Control measurement on the same server through the authenticated data API:

  • GET /api/v1/data/ats_job?$filter=[{"field":"status","operator":"equals","value":"published"}]400 INVALID_FILTER (same message)
  • GET /api/v1/data/ats_job?$filter=[["status","=","published"]]200

So the failure is not the picker filter alone: the q branch builds the identical object shape, which means the route 400s for ANY non-empty search, with or without a declared publicPicker.filter. Only the degenerate q= + no-filter call (filters: []) can succeed.

Expected

Either the route lowers ViewFilterRule objects to the parser's triple grammar before calling findData (there is already a spec-side normaliser for view filter rules), or findData accepts the ViewFilterRule object dialect on query.filters. The FormFieldPublicPickerSchema.filter describe text promises "Same { field, operator, value } dialect as list-view filters", which is exactly what the parser refuses.

Related


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:clipriority:p1High: required for production / M2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions