Budibase/budibase · error · HTTPError

view is missing queryUI field

Error message

view is missing queryUI field

What it means

ensureQueryUISet normalizes a ViewV2 so that its `queryUI` field is populated from `query`. Views persist filters as either LegacyFilter[] (array form) or SearchFilters (object form). The code can only convert the legacy array form to queryUI; if `query` is a non-empty object (SearchFilters) but `queryUI` is missing, there is no safe conversion, so it throws a 400 HTTPError.

Source

Thrown at packages/server/src/sdk/workspace/views/utils.ts:38

}

export function ensureQueryUISet(viewArg: Readonly<ViewV2>): ViewV2 {
  const view = cloneDeep<ViewV2>(viewArg)
  if (!view.queryUI && view.query && !isEmptyObject(view.query)) {
    if (!Array.isArray(view.query)) {
      // In practice this should not happen. `view.query`, at the time this code
      // goes into the codebase, only contains LegacyFilter[] in production.
      // We're changing it in the change that this comment is part of to also
      // include SearchFilters objects. These are created when we receive an
      // update to a ViewV2 that contains a queryUI and not a query field. We
      // can convert UISearchFilter (the type of queryUI) to SearchFilters,
      // but not LegacyFilter[], they are incompatible due to UISearchFilter
      // and SearchFilters being recursive types.
      //
      // So despite the type saying that `view.query` is a LegacyFilter[] |
      // SearchFilters, it will never be a SearchFilters when a `view.queryUI`
      // is specified, making it "safe" to throw an error here.
      throw new HTTPError("view is missing queryUI field", 400)
    }

    view.queryUI = utils.processSearchFilters(view.query)
  }
  return view
}

export function ensureQuerySet(viewArg: Readonly<ViewV2>): ViewV2 {
  const view = cloneDeep<ViewV2>(viewArg)
  // We consider queryUI to be the source of truth, so we don't check for the
  // presence of query here. We will overwrite it regardless of whether it is
  // present or not.
  if (view.queryUI && !isEmptyObject(view.queryUI)) {
    view.query = dataFilters.buildQuery(view.queryUI)
  }
  return view
}

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Check the offending view doc in the workspace DB and either add a `queryUI` field (UISearchFilter form) or convert `query` back to the LegacyFilter[] array form
  2. Re-save the view through the builder UI so the server writes both `query` and `queryUI` via ensureQuerySet
  3. If programmatically creating views via the API, send `queryUI` (not a SearchFilters-shaped `query`)
  4. Delete and recreate the corrupted view if its filters are not needed

Example fix

// before (API payload that triggers the error)
{ "name": "MyView", "query": { "equal": { "status": "live" } } }
// after
{ "name": "MyView", "queryUI": { "onGroups": [{ "operator": "equal", "field": "status", "value": "live" }] } }
Defensive patterns

Strategy: validation

Validate before calling

function canProcessView(view) {
  const hasQuery = view.query && Object.keys(view.query).length > 0
  return !(hasQuery && !Array.isArray(view.query) && !view.queryUI)
}
if (!canProcessView(view)) throw new Error('View needs queryUI or array-form query')

Type guard

function hasConvertibleQuery(view) {
  return !view.query || Array.isArray(view.query) || !!view.queryUI
}

Try / catch

try {
  const enriched = await sdk.views.get(viewId)
} catch (e) {
  if (e?.status === 400 && e?.message?.includes('queryUI')) {
    // recreate or repair the view doc
  } else throw e
}

Prevention

When it happens

Trigger: Any of processTable, get, getEnriched, create, update, or enrichedViews loads/accepts a view whose `query` is a non-empty SearchFilters object while `queryUI` is undefined/null. This happens when a view doc was persisted with SearchFilters-shaped query data but without the queryUI field (e.g. written by code that bypassed ensureQuerySet, hand-edited docs, or docs from a version before queryUI existed).

Common situations: Upgrading Budibase and hitting views migrated from older versions; importing/exporting apps where view docs were edited externally; writing directly to CouchDB or via scripts that set `query` without `queryUI`; partial API calls that sent `query` instead of `queryUI` on view create/update.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/ff13b03d2d35eda4. Report an issue: GitHub.