cube-js/cube · error · UserError

Expected reference to the model in format ref('model_name')…

Error message

Expected reference to the model in format ref('model_name') but found '${metricDef.model}'

What it means

Each metric in the dbt manifest must reference its underlying model via a model string in the exact format ref('model_name'). loadMetricCubesFromDbtProject matches metricDef.model against the regex /^ref\('(\S+)'\)$/ and throws this UserError when the string does not conform, since the model name cannot be extracted.

Solutions

  1. Fix the metric definition in the dbt project so model uses ref('model_name') syntax and re-run dbt compile
  2. Check for quoting/whitespace issues: single quotes required inside ref(), no leading/trailing spaces
  3. Regenerate manifest.json with the same dbt CLI version rather than hand-editing it
  4. If tooling rewrites the manifest, ensure it preserves the ref('model_name') format

Example fix

// before (metric in manifest)
"model": "model.my_project.orders"
// after
"model": "ref('orders')"
Defensive patterns

Strategy: validation

Validate before calling

for (const m of Object.values(manifest.metrics)) {
  if (!/^ref\('(\S+)'\)$/.test(m.model)) {
    throw new Error(`Metric ${m.name}: model must match ref('model_name'), got: ${m.model}`);
  }
}

Type guard

function hasRefModel(metricDef) {
  return typeof metricDef.model === 'string' && /^ref\('(\S+)'\)$/.test(metricDef.model);
}

Try / catch

try {
  await dbt.loadMetricCubesFromDbtProject(projectPath, opts);
} catch (e) {
  if (e.message.startsWith("Expected reference to the model")) {
    console.error('Fix metric model refs in dbt project and re-run dbt compile:', e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: A manifest metric's model field is e.g. 'model.my_project.my_model', a bare model name without ref(), uses double quotes ref("m"), contains extra spaces, or was produced by tooling that writes resolved model references instead of the ref() syntax.

Common situations: Manually editing or programmatically post-processing manifest.json before Cube reads it; older/newer dbt versions writing a different model reference format; third-party tooling that rewrites manifests; copy-pasting metric definitions with the wrong model syntax.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/847fdf2702401ba0. Report an issue: GitHub.

Appendix: source

Thrown at packages/cubejs-dbt-schema-extension/src/Dbt.ts:152

export class Dbt extends AbstractExtension {
  public async loadMetricCubesFromDbtProject(projectPath: string, options: DbtLoadOptions): Promise<{ [cubeName: string]: any }> {
    const dbtProjectPath = path.join(projectPath, 'dbt_project.yml');
    if (!(await fs.pathExists(dbtProjectPath))) {
      throw new UserError(`'${dbtProjectPath}' was not found. Please make sure '${projectPath}' is a path to the dbt project`);
    }
    // TODO read target path from dbt_project.yml
    const manifestPath = path.join(projectPath, 'target', 'manifest.json');
    if (!(await fs.pathExists(manifestPath))) {
      throw new UserError(`'${manifestPath}' was not found. Please run 'dbt compile' in '${projectPath}'`);
    }
    const manifest = <DbtManifest>(await fs.readJSON(manifestPath));
    Object.keys(manifest.metrics).forEach(metric => {
      const regex = /^ref\('(\S+)'\)$/;
      const metricDef = manifest.metrics[metric];
      const match = metricDef.model.match(regex);
      if (!match) {
        throw new UserError(`Expected reference to the model in format ref('model_name') but found '${metricDef.model}'`);
      }
      // eslint-disable-next-line prefer-destructuring
      const modelName = match[1];
      metricDef.model = modelName.indexOf('.') !== -1 ? modelName : `model.${metricDef.package_name}.${modelName}`;
    });
    return this.loadMetricCubesFromNormalizedManifest(manifest, manifestPath, options);
  }

  public async loadMetricCubesFromDbtCloud(jobId: string | number, authToken: string, options: DbtLoadOptions): Promise<{ [cubeName: string]: any }> {
    const response = await fetch('https://metadata.cloud.getdbt.com/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
        Authorization: `Bearer ${authToken}`
      },
      body: JSON.stringify({ query: loadModelsQuery, variables: { jobId } })
    });

View on GitHub (pinned to 7d981676b3)