windmill-labs/windmill · error · DbtPathCollisionError

${project} and ${other} deploy to the same path, so pushing

Error message

${project} and ${other} deploy to the same path, so pushing either one replaces the other's script. Keep one: move the dbt project to a path of its own, or remove ${other}.

What it means

A dbt project folder and an ordinary script file resolve to the same remote script path, so pushing either would overwrite the other's script on the server. The CLI detects the collision (DbtPathCollisionError, a subclass of UnresolvableScriptContentFileError) and refuses to deploy rather than silently replacing a script.

Source

Thrown at cli/src/commands/script/script.ts:1187

          });
      })
    )
  )
    .filter((x) => x.file)
    .map((x) => x.path);
  // A dbt project's descriptor is OPTIONAL, so `dbt_project.yml` is what says a
  // dbt script lives at this path — the descriptor is often absent from the
  // candidates above while the project is perfectly real. Asked BEFORE the
  // counts below: a project beside an ordinary script is not "one candidate",
  // it is two scripts claiming one remote path, and returning the ordinary one
  // deploys it OVER the dbt script on the next push of any model.
  const dbtCandidate = toCandidate("__dbt/" + DBT_DESCRIPTOR_NAME);
  const dbtProject = await collidingDbtProject(
    dbtCandidate.slice(0, -("__dbt/" + DBT_DESCRIPTOR_NAME).length),
  );
  const nonDbtCandidates = validCandidates.filter((c) => c !== dbtCandidate);
  if (dbtProject && nonDbtCandidates.length > 0) {
    throw dbtPathCollisionError(dbtProject, nonDbtCandidates.join(", "));
  }
  if (validCandidates.length > 1) {
    throw new UnresolvableScriptContentFileError(
      `Multiple script files found next to ${filePath}: ${validCandidates.join(", ")} — ` +
        `cannot tell which one the metadata belongs to. Keep exactly one.`
    );
  }
  if (validCandidates.length < 1) {
    // Resolving to the absent descriptor keeps one content path for every
    // caller; reading it yields an empty descriptor.
    if (dbtProject) {
      return dbtCandidate;
    }
    throw new UnresolvableScriptContentFileError(
      `No script file found next to ${filePath} — a script cannot be deployed from its metadata alone. ` +
        `Add the matching script file (e.g. ${toCandidate(".ts")} or ${toCandidate(
          ".py"
        )}) or remove ${filePath}.`

View on GitHub (pinned to e474e8803c)

Solutions

  1. Move the dbt project to a path of its own (its own folder not shared with the script file)
  2. Or remove/move the ordinary script file (e.g. the stray .py/.ts) that collides with the dbt project
  3. Re-run the push after only one content source remains at that path

Example fix

// before
myflow/my_script.py
myflow/dbt_project.yml   # both deploy to myflow
// after
myflow/my_script.py
myflow_dbt/dbt_project.yml  # separate path
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from 'deno/fs'; const hasDbt = await stat(`${base}/dbt_project.yml`).then(() => true).catch(() => false); const stray = ['.py','.ts','.go','.sh'].filter(async ext => await stat(base.replace(/\.script\.(yaml|json|lock)$/, ext)).then(s => s.isFile).catch(() => false)); if (hasDbt && stray.length) throw new Error(`dbt project and ${stray} share one path`);

Type guard

import { DbtPathCollisionError } from './script.ts'; function isDbtCollision(e: unknown): e is DbtPathCollisionError { return e instanceof DbtPathCollisionError; }

Try / catch

try { await pushAll(); } catch (e) { if (isDbtCollision(e)) { console.error(`Fix the path collision locally: ${e.message}`); Deno.exit(1); } throw e; }

Prevention

When it happens

Trigger: findContentFile finds a dbt_project.yml at a base path AND at least one non-dbt content candidate (e.g. <base>.py or <base>.ts) next to the metadata; both map to the same remote path.

Common situations: A dbt project was initialized in a folder that already contained a script file; a script file was added into an existing dbt project folder; git-sync pulls merged both into one directory.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/6382ada2ad289fbb. Report an issue: GitHub.