{"record":{"id":"428693dab6d40c2b","repo":"Budibase/budibase","slug":"calculation-fields-are-not-allowed-in-non-calculat","errorCode":null,"errorMessage":"Calculation fields are not allowed in non-calculation views","messagePattern":"Calculation fields are not allowed in non-calculation views","errorType":"validation","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/views/index.ts","lineNumber":226,"sourceCode":"      throw new HTTPError(\n        `Grouping by fields of type \"${targetSchema.type}\" is not supported`,\n        400\n      )\n    }\n  }\n}\n\nasync function guardViewSchema(\n  tableId: string,\n  view: Omit<ViewV2, \"id\" | \"version\">\n) {\n  const table = await sdk.tables.getTable(tableId)\n\n  if (helpers.views.isCalculationView(view)) {\n    await guardCalculationViewSchema(table, view)\n  } else {\n    if (helpers.views.hasCalculationFields(view)) {\n      throw new HTTPError(\n        \"Calculation fields are not allowed in non-calculation views\",\n        400\n      )\n    }\n  }\n\n  await checkReadonlyFields(table, view)\n\n  if (!helpers.views.isCalculationView(view)) {\n    checkRequiredFields(table, view)\n  }\n\n  // Falling back to the table's display column here means a view whose\n  // display column no longer exists can still be saved, rather than every\n  // save being rejected by the display column check\n  ensureValidPrimaryDisplay(view, table)\n\n  checkDisplayField(view)","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/views/index.ts#L208-L244","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Remove all calculation fields from the view schema.","Or mark/create the view as a calculation view if aggregations are intended.","Or move the aggregation fields into a separate calculation view."],"exampleFix":"// before\nconst view = { name: \"v\", type: \"table\", schema: { total: { visible: true, readonly: false, /* calculation field */ } } }\n// after\nconst view = { name: \"v\", type: \"table\", schema: { total: { visible: true, readonly: false } } }","handlingStrategy":"validation","validationCode":"if (!helpers.views.isCalculationView(view) && helpers.views.hasCalculationFields(view)) {\n  Object.keys(view.schema).forEach(k => delete view.schema[k])\n}","typeGuard":"function hasNoCalcFields(view: Omit<ViewV2, \"id\" | \"version\">): boolean {\n  return helpers.views.isCalculationView(view) || !helpers.views.hasCalculationFields(view)\n}","tryCatchPattern":"try {\n  await sdk.views.update(view)\n} catch (e) {\n  if (e instanceof HTTPError && e.status === 400 && e.message === \"Calculation fields are not allowed in non-calculation views\") {\n    // strip calc fields and retry once\n  } else { throw e }\n}","preventionTips":["Only add calculation fields to views created as calculation views","When changing view type, rebuild the schema from scratch","Never copy schemas between calculation and plain views verbatim"],"tags":["validation","views","schema"],"backgroundTag":"invalid-view-configuration","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}