Budibase/budibase · error · HTTPError

Calculation fields are not allowed in non-calculation views

Error message

Calculation fields are not allowed in non-calculation views

What it means

guardViewSchema rejects view payloads where a NON-calculation view includes calculation fields. Calculation fields (aggregations like sums/counts) are only valid on calculation views; placing them on a regular view is an invalid config, so a 400 is thrown.

Source

Thrown at packages/server/src/sdk/workspace/views/index.ts:226

      throw new HTTPError(
        `Grouping by fields of type "${targetSchema.type}" is not supported`,
        400
      )
    }
  }
}

async function guardViewSchema(
  tableId: string,
  view: Omit<ViewV2, "id" | "version">
) {
  const table = await sdk.tables.getTable(tableId)

  if (helpers.views.isCalculationView(view)) {
    await guardCalculationViewSchema(table, view)
  } else {
    if (helpers.views.hasCalculationFields(view)) {
      throw new HTTPError(
        "Calculation fields are not allowed in non-calculation views",
        400
      )
    }
  }

  await checkReadonlyFields(table, view)

  if (!helpers.views.isCalculationView(view)) {
    checkRequiredFields(table, view)
  }

  // Falling back to the table's display column here means a view whose
  // display column no longer exists can still be saved, rather than every
  // save being rejected by the display column check
  ensureValidPrimaryDisplay(view, table)

  checkDisplayField(view)

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Remove all calculation fields from the view schema.
  2. Or mark/create the view as a calculation view if aggregations are intended.
  3. Or move the aggregation fields into a separate calculation view.

Example fix

// before
const view = { name: "v", type: "table", schema: { total: { visible: true, readonly: false, /* calculation field */ } } }
// after
const view = { name: "v", type: "table", schema: { total: { visible: true, readonly: false } } }
Defensive patterns

Strategy: validation

Validate before calling

if (!helpers.views.isCalculationView(view) && helpers.views.hasCalculationFields(view)) {
  Object.keys(view.schema).forEach(k => delete view.schema[k])
}

Type guard

function hasNoCalcFields(view: Omit<ViewV2, "id" | "version">): boolean {
  return helpers.views.isCalculationView(view) || !helpers.views.hasCalculationFields(view)
}

Try / catch

try {
  await sdk.views.update(view)
} catch (e) {
  if (e instanceof HTTPError && e.status === 400 && e.message === "Calculation fields are not allowed in non-calculation views") {
    // strip calc fields and retry once
  } else { throw e }
}

Prevention

When it happens

Trigger: sdk.views.create or sdk.views.update called with a view where helpers.views.isCalculationView(view) is false but helpers.views.hasCalculationFields(view) is true (view.schema entries flagged as calculation fields).

Common situations: Manually editing exported view JSON; API clients copying schema from a calculation view onto a plain view; UI state drift after switching view type without stripping calculation fields.

Related errors


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