Every time you run dart through the shim, or run dvm dart
or dvm exec, dvm answers one question: which SDK applies in this directory, right now?
It answers it with five rules, in this order. The first one that matches wins.
The five rules#
1. The DVM_DART_VERSION environment variable#
DVM_DART_VERSION=3.9.0 dart --version
If this variable is set and non-empty, it wins over everything on disk. It is the escape hatch for CI and for one-off commands — see Using dvm in CI.
Its value is a pin like any other: a version, an alias, or a channel name.
2. The nearest .dvmrc#
dvm walks up from the current directory to the filesystem root and uses the first .dvmrc
it finds. This is the normal case and the one you want most of the time.
Nearest wins, which is what makes a monorepo with a per-package override work.
3. The global default#
global in ~/.dvm/config.json, set with dvm global. This is what applies in directories that pin nothing.
4. The next real dart on PATH#
When no rule above matched, dvm scans PATH for a dart that is not one of its own shims
and hands off to that.
This rule is what keeps the rest of your machine working as it did. A directory that pins nothing and has no global default runs whatever
dart you already had.
5. A clear error#
When no rule matched, dvm says so and lists everything it checked:
No Dart SDK applies in /Users/you/scratch.
- DVM_DART_VERSION is not set
- no .dvmrc here or in any parent directory
- no "global" in /Users/you/.dvm/config.json
- no dart on PATH that is not a dvm shim
Pin one for this project: dvm use <version>
Or set a machine default: dvm global <version>
Seeing which rule answered#
This is the point of the design, and it is why dvm which reports the
rule and not just the path:
dvm which
/Users/you/.dvm/versions/3.13.2/bin/dart
Dart 3.13.2
Chosen by rule 2 of 5: pinned by /Users/you/code/api/.dvmrc.
SDK: /Users/you/.dvm/versions/3.13.2
Every rule has its own sentence:
| Rule | What dvm which says |
|---|---|
| 1 |
Chosen by rule 1 of 5: DVM_DART_VERSION is set in the environment, which overrides everything on disk.
|
| 2 | Chosen by rule 2 of 5: pinned by <path>/.dvmrc. |
| 3 |
Chosen by rule 3 of 5: no .dvmrc applies here, so the global default in <path>/config.json was used.
|
| 4 |
Chosen by rule 4 of 5: nothing pins a version here and no global default is set, so this is the next dart on PATH, found in <dir>.
|
When a pin was indirect, the hop is spelled out too:
It says "work", an alias for 3.13.2 in /Users/you/.dvm/config.json.
dvm list prints a one-line version of the same thing under the list of installed SDKs.
Every resolution is two small file reads#
Every dart invocation on the machine pays for this path once the shim is installed, so it stays as cheap as it can be: at most two small files (.dvmrc
and config.json) and one stat. That is what keeps dart through the shim as fast as
dart without it — on a plane and behind a firewall as much as anywhere else.
A channel name is answered from config.json here: stable
means the version recorded when you last ran dvm install stable. Asking the archive what
stable means today is its own command, run when you want it.
The shim-skipping in rule 4#
Rule 4 skips dvm's own shims, and that is what makes rule 4 terminate.
The shim is ~/.dvm/shims/dart, you are told to put that directory first on PATH, and its body is
exec dvm exec dart "$@". A scan that took the first dart it found would find the shim, run it, and re-enter resolution — forking until the machine gave up.
So rule 4 skips three things:
-
the shims directory itself, however it is spelled in
PATH(~/.dvm/shims,$HOME/.dvm/./shims/and the absolute path are all the same directory, and all three turn up in realPATHs), -
a symlink pointing into the shims directory — what
ln -s ~/.dvm/shims/dart ~/.local/bin/dartleaves behind, - a copy of a shim, recognised by its contents, which no path comparison could catch.
When a pin names a version you do not have#
Rules 1, 2 and 3 name a version, so they can match and still have work left. When your .dvmrc
pins a version you have not installed, dvm names it and gives you the command that fills the gap:
dvm: Dart 3.9.0 is pinned by /Users/you/code/api/.dvmrc, but it is not installed. Run: dvm install 3.9.0
You get the SDK the project asked for, or a message naming the one it asked for. That is the guarantee the whole tool is built to give.
Debugging checklist#
When you want to know how dart --version reached its answer:
-
which dart— is it~/.dvm/shims/dart? If not, yourPATHorder is wrong. dvm which— which rule answered, and from which file?-
dvm doctor— PATH order, shim health, a shadowing shell function, config validity. -
echo $DVM_DART_VERSION— rule 1 beats everything, including the.dvmrcyou are looking at.