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
- Move the dbt project to a path of its own (its own folder not shared with the script file)
- Or remove/move the ordinary script file (e.g. the stray .py/.ts) that collides with the dbt project
- 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
- Never mix a dbt project folder and script files under one base path
- Layout rule: one content source (script OR dbt project) per deploy path
- Add a repo lint that flags dbt_project.yml with sibling script content files
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
- ${relPath} is ${Math.ceil(size / 1024 / 1024)} MB, over the
- Unknown kind: ${kind}
- Deletion not supported for kind: ${kind}
- Skipping ${relPath}: a local secrets file is not part of the
- Skipping ${relPath}: not a text file, so it is not part of t
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/6382ada2ad289fbb.
Report an issue: GitHub.