{"record":{"id":"ab217622f9708d83","repo":"cube-js/cube","slug":"steps-are-not-defined","errorCode":null,"errorMessage":"steps are not defined","messagePattern":"steps are not defined","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/extensions/Funnels.ts","lineNumber":17,"sourceCode":"import inflection from 'inflection';\nimport { AbstractExtension } from './extension.abstract';\n\nexport class Funnels extends AbstractExtension {\n  // TODO check timeToConvert is absent on first step\n  // TODO name can be a title\n  public eventFunnel(funnelDefinition) {\n    if (!funnelDefinition.userId || !funnelDefinition.userId.sql) {\n      throw new Error('userId.sql is not defined'); // TODO schema check\n    }\n\n    if (!funnelDefinition.time || !funnelDefinition.time.sql) {\n      throw new Error('time.sql is not defined'); // TODO schema check\n    }\n\n    if (!funnelDefinition.steps || !funnelDefinition.steps.length) {\n      throw new Error('steps are not defined'); // TODO schema check\n    }\n\n    return this.cubeFactory({\n      sql: () => {\n        const eventJoin =\n          funnelDefinition.steps.map((s, i) => this.eventCubeJoin(funnelDefinition, s, funnelDefinition.steps[i - 1]));\n        const userIdColumnsAndTime =\n          funnelDefinition.steps.map(s => `${this.eventsTableName(s)}.user_id ${this.stepUserIdColumnName(s)}`)\n            .concat([`${this.eventsTableName(funnelDefinition.steps[0])}.t`]).join(',\\n');\n        return `WITH joined_events AS (\n    select\n    ${userIdColumnsAndTime}\n    FROM\n${eventJoin.join('\\nLEFT JOIN\\n')}\n  )\n  select user_id, first_step_user_id, step, max(t) t from (\n    ${funnelDefinition.steps.map(s => this.stepSegmentSelect(funnelDefinition, s)).join('\\nUNION ALL\\n')}\n  ) as event_steps GROUP BY 1, 2, 3`;","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/extensions/Funnels.ts#L1-L35","documentation":"The Funnels schema extension requires funnel definitions to include a steps array defining the funnel sequence; the error fires in eventFunnel when steps is absent or empty. Like the other funnel guards it is a plain schema validation check in the extension, so the fix belongs in the data model, not in query parameters.","triggerScenarios":"eventFunnel called with steps undefined, steps: [], or steps set to a non-array value.","commonSituations":"Defining a funnel placeholder before filling in steps; YAML indentation flattening the steps list; filtering logic accidentally producing an empty steps array.","solutions":["Provide at least two step definitions: steps: [{ event: 'signup' }, { event: 'purchase' }]","Ensure steps is a YAML sequence (each step prefixed with '- ') in YAML schemas","Guard dynamic step generation so empty arrays never reach eventFunnel"],"exampleFix":"// before\neventFunnel({\n  userId: { sql: `${CUBE}.user_id` },\n  time: { sql: `${CUBE}.timestamp` },\n  steps: []\n});\n// after\neventFunnel({\n  userId: { sql: `${CUBE}.user_id` },\n  time: { sql: `${CUBE}.timestamp` },\n  steps: [\n    { event: 'signup' },\n    { event: 'purchase' }\n  ]\n});","handlingStrategy":"validation","validationCode":"const hasSteps = (f) => Array.isArray(f?.steps) && f.steps.length > 0;","typeGuard":"const hasSteps = (f) => Array.isArray(f?.steps) && f.steps.length >= 1 && f.steps.every(s => s && typeof s === 'object');","tryCatchPattern":"try {\n  funnelCube.eventFunnel(def);\n} catch (e) {\n  if (e.message === 'steps are not defined') {\n    console.error('Provide a non-empty steps array in the funnel definition');\n  }\n  throw e;\n}","preventionTips":["Never pass an empty steps array — build funnels only when steps exist","In YAML, ensure each step is a '- ' list item","Guard dynamically generated step lists before calling eventFunnel"],"tags":["funnel","schema-validation","extension"],"backgroundTag":"missing-required-argument","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}