{"record":{"id":"e1857a5273cdad54","repo":"Budibase/budibase","slug":"must-supply-either-a-view-id-or-a-table-id","errorCode":null,"errorMessage":"Must supply either a view ID or a table ID","messagePattern":"Must supply either a view ID or a table ID","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/rows/search.ts","lineNumber":70,"sourceCode":"      sortOrder: options.sortOrder,\n      sortType: options.sortType,\n      limit: options.limit,\n      bookmark: options.bookmark,\n      paginate: options.paginate,\n      fields: options.fields,\n      countRows: options.countRows,\n    })\n\n    let source: Table | ViewV2\n    let table: Table\n    if (options.viewId) {\n      source = await sdk.views.get(options.viewId)\n      table = await sdk.views.getTable(source)\n    } else if (options.tableId) {\n      source = await sdk.tables.getTable(options.tableId)\n      table = source\n    } else {\n      throw new Error(`Must supply either a view ID or a table ID`)\n    }\n\n    const isExternalTable = isExternalTableID(table._id!)\n\n    if (options.query) {\n      const visibleFields = (\n        options.fields || Object.keys(table.schema)\n      ).filter(field => table.schema[field]?.visible !== false)\n\n      const queryableFields = await getQueryableFields(table, visibleFields)\n      validateFilters(options.query, queryableFields)\n    } else {\n      options.query = {}\n    }\n\n    if (context) {\n      options.query = await enrichSearchContext(options.query, context)\n    }","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/rows/search.ts#L52-L88","documentation":"The rows `search` function requires exactly one data source: a view ID or a table ID. When neither option is supplied it throws a plain Error telling the caller to provide one.","triggerScenarios":"Calling sdk.rows.search({}) or omitting both `viewId` and `tableId` in the options object.","commonSituations":"Programmatically constructed search options where the ID variable is undefined; API requests missing query/body params; refactors that renamed the option keys.","solutions":["Pass options.tableId with a valid table ID","Or pass options.viewId with a valid view ID","Log/inspect the options object to confirm the ID key is spelled correctly and populated"],"exampleFix":"// before\nawait sdk.rows.search({ query: {} })\n// after\nawait sdk.rows.search({ tableId: 'ta_...', query: {} })","handlingStrategy":"validation","validationCode":"if (!options.tableId && !options.viewId) throw new Error('search requires tableId or viewId')","typeGuard":"function hasSearchSource(o: { tableId?: string; viewId?: string }): o is { tableId: string } | { viewId: string } { return !!o.tableId || !!o.viewId }","tryCatchPattern":"try { await sdk.rows.search(opts) } catch (e) { if (e.message.includes('Must supply either a view ID or a table ID')) { /* fix options */ } else throw e }","preventionTips":["Type search options so tableId or viewId is required","Default tableId from route/context before calling","Validate options objects constructed dynamically"],"tags":["missing-argument","search","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}