diff --git a/beets/config_default.yaml b/beets/config_default.yaml
index 0fd6eb592..c75778b80 100644
--- a/beets/config_default.yaml
+++ b/beets/config_default.yaml
@@ -44,6 +44,7 @@ replace:
     '^\s+': ''
     '^-': _
 path_sep_replace: _
+drive_sep_replace: _
 asciify_paths: false
 art_filename: cover
 max_filename_length: 0
diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py
index b13f2638a..46b47a2e1 100755
--- a/beets/dbcore/db.py
+++ b/beets/dbcore/db.py
@@ -19,6 +19,7 @@ from __future__ import division, absolute_import, print_function
 
 import time
 import os
+import re
 from collections import defaultdict
 import threading
 import sqlite3
@@ -84,6 +85,11 @@ class FormattedMapping(Mapping):
 
         if self.for_path:
             sep_repl = beets.config['path_sep_replace'].as_str()
+            sep_drive = beets.config['drive_sep_replace'].as_str()
+
+            if re.match(r'^\w:', value):
+                value = re.sub(r'(?<=^\w):', sep_drive, value)
+
             for sep in (os.path.sep, os.path.altsep):
                 if sep:
                     value = value.replace(sep, sep_repl)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 0f41c38ec..64e6ab85c 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -235,6 +235,9 @@ Fixes:
 * :doc:`/plugins/ipfs`: Fix Python 3 compatibility.
   Thanks to :user:`musoke`.
   :bug:`2554`
+* Fix a bug that caused metadata starting with something resembling a drive
+  letter to be incorrectly split into an extra directory after the colon.
+  :bug:`3685`
 
 For plugin developers:
 

Sanitize colons in drive-letter-like positions in Windows filenames like path separators
I have an album containing an artist named "F:A.R.". When doing any activity that involves moving this artist, the file is not moved to "M:\F:A.R." (I'm using unicode replacements instead of underscores), but to "M:\F:\A.R.".

### Problem

Running this command in verbose (`-vv`) mode:

```sh
~ ❯ beet -vv move "artist::^F.A.R"
user configuration: C:\Users\Gunther Schmidl\AppData\Roaming\beets\config.yaml
data directory: C:\Users\Gunther Schmidl\AppData\Roaming\beets
plugin paths:
Sending event: pluginload
lyrics: Disabling google source: no API key configured.
library database: M:\lib.db
library directory: M:\
Sending event: library_opened
Moving 1 item.
moving: M:\F꞉A․R․ ／ Muslimgauze\[1990] Manipulation (Live Extracts) ／ Death of Saint Jarnaii Singh Bhindranwaie\01-01 - Manipulation (Live Extracts).mp3
Sending event: before_item_moved
Sending event: item_moved
Sending event: database_change
Sending event: database_change
Sending event: cli_exit
```

Led to this problem:

Here's what it looks like in the database: https://i.imgur.com/Z5lvGqE.png

Here's a link to the music files that trigger the bug (if relevant):

https://www.dropbox.com/s/pastihu349z6kce/01-01%20-%20Manipulation%20%28Live%20Extracts%29.mp3?dl=0

### Setup

* OS: Windows 10 v2004
* Python version: 3.8.0
* beets version: latest trunk, but also happens in latest released version
* Turning off plugins made problem go away (yes/no): no

My configuration (output of `beet config`) is:

```yaml
lyrics:
    bing_lang_from: []
    auto: yes
    bing_client_secret: REDACTED
    bing_lang_to:
    google_API_key: REDACTED
    google_engine_ID: REDACTED
    genius_api_key: REDACTED
    fallback:
    force: no
    local: no
    sources:
    - google
    - lyricwiki
    - musixmatch
    - genius
directory: M:/
library: M:/lib.db

paths:
    default: $albumartist/[$year] $album%aunique{}/$disc-$track - $title
    singleton: Non-Album/$artist/$title
    comp: Compilations/[$year] $album%aunique{}/$disc-$track - $title

plugins: web mbsync discogs chroma lastgenre duplicates lyrics
lastgenre:
    source: artist
    force: no
    whitelist: yes
    min_weight: 10
    count: 1
    fallback:
    canonical: no
    auto: yes
    separator: ', '
    prefer_specific: no

replace:
    \\: "\uFF3C"
    /: "\uFF0F"
    \.{3}: "\u2026"
    \.: "\u2024"
    '[\x00-\x1f]': ''
    <: "\uFE64"
    '>': "\uFE65"
    ':': "\uA789"
    '"': "\uFF02"
    \?: "\uFF1F"
    \*: "\u204E"
    \|: "\u2502"
    \s+$: ''
    ^\s+: ''
    ^-: "\u2012"
path_sep_replace: "\uFF0F"
discogs:
    apikey: REDACTED
    apisecret: REDACTED
    tokenfile: discogs_token.json
    source_weight: 0.5
    user_token: REDACTED
    separator: ', '
    index_tracks: no
web:
    host: 127.0.0.1
    port: 8337
    cors: ''
    cors_supports_credentials: no
    reverse_proxy: no
    include_paths: no
duplicates:
    album: no
    checksum: ''
    copy: ''
    count: no
    delete: no
    format: ''
    full: no
    keys: []
    merge: no
    move: ''
    path: no
    tiebreak: {}
    strict: no
    tag: ''
chroma:
    auto: yes
```

diff --git a/test/test_files.py b/test/test_files.py
index f31779672..13a8b4407 100644
--- a/test/test_files.py
+++ b/test/test_files.py
@@ -102,6 +102,25 @@ class MoveTest(_common.TestCase):
         self.i.move()
         self.assertEqual(self.i.path, old_path)
 
+    def test_move_file_with_colon(self):
+        self.i.artist = u'C:DOS'
+        self.i.move()
+        self.assertIn('C_DOS', self.i.path.decode())
+
+    def test_move_file_with_multiple_colons(self):
+        print(beets.config['replace'])
+        self.i.artist = u'COM:DOS'
+        self.i.move()
+        self.assertIn('COM_DOS', self.i.path.decode())
+
+    def test_move_file_with_colon_alt_separator(self):
+        old = beets.config['drive_sep_replace']
+        beets.config["drive_sep_replace"] = '0'
+        self.i.artist = u'C:DOS'
+        self.i.move()
+        self.assertIn('C0DOS', self.i.path.decode())
+        beets.config["drive_sep_replace"] = old
+
     def test_read_only_file_copied_writable(self):
         # Make the source file read-only.
         os.chmod(self.path, 0o444)

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
anyio==4.9.0
beautifulsoup4==4.13.3
-e git+https://github.com/beetbox/beets.git@a2f66a042727964e18d698a7b1676a802b5f7050#egg=beets
blinker==1.9.0
certifi==2025.1.31
charset-normalizer==3.4.1
click==8.1.8
confuse==2.0.1
coverage==7.8.0
discogs-client==2.3.0
exceptiongroup==1.2.2
execnet==2.1.1
filetype==1.2.0
Flask==3.1.0
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
idna==3.10
importlib_metadata==8.6.1
iniconfig==2.1.0
itsdangerous==2.2.0
jellyfish==1.1.3
Jinja2==3.1.6
MarkupSafe==3.0.2
mediafile==0.13.0
mock==5.2.0
munkres==1.1.4
musicbrainzngs==0.7.1
mutagen==1.47.0
oauthlib==3.2.2
packaging==24.2
pluggy==1.5.0
pylast==5.5.0
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
python-mpd2==3.1.1
pyxdg==0.28
PyYAML==6.0.2
rarfile==4.2
requests==2.32.3
requests-oauthlib==2.0.0
responses==0.25.7
six==1.17.0
sniffio==1.3.1
soupsieve==2.6
tomli==2.2.1
typing_extensions==4.13.0
Unidecode==1.3.8
urllib3==2.3.0
Werkzeug==3.1.3
zipp==3.21.0

name: beets
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - anyio==4.9.0
      - beautifulsoup4==4.13.3
      - blinker==1.9.0
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - click==8.1.8
      - confuse==2.0.1
      - coverage==7.8.0
      - discogs-client==2.3.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - filetype==1.2.0
      - flask==3.1.0
      - h11==0.14.0
      - httpcore==1.0.7
      - httpx==0.28.1
      - idna==3.10
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - itsdangerous==2.2.0
      - jellyfish==1.1.3
      - jinja2==3.1.6
      - markupsafe==3.0.2
      - mediafile==0.13.0
      - mock==5.2.0
      - munkres==1.1.4
      - musicbrainzngs==0.7.1
      - mutagen==1.47.0
      - oauthlib==3.2.2
      - packaging==24.2
      - pluggy==1.5.0
      - pylast==5.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - python-mpd2==3.1.1
      - pyxdg==0.28
      - pyyaml==6.0.2
      - rarfile==4.2
      - requests==2.32.3
      - requests-oauthlib==2.0.0
      - responses==0.25.7
      - six==1.17.0
      - sniffio==1.3.1
      - soupsieve==2.6
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - unidecode==1.3.8
      - urllib3==2.3.0
      - werkzeug==3.1.3
      - zipp==3.21.0
prefix: /opt/conda/envs/beets

test/test_files.py::MoveTest::test_move_file_with_colon_alt_separator
test/test_files.py::MoveTest::test_copy_already_at_destination test/test_files.py::MoveTest::test_copy_arrives test/test_files.py::MoveTest::test_copy_does_not_depart test/test_files.py::MoveTest::test_hardlink_arrives test/test_files.py::MoveTest::test_hardlink_changes_path test/test_files.py::MoveTest::test_hardlink_does_not_depart test/test_files.py::MoveTest::test_link_arrives test/test_files.py::MoveTest::test_link_changes_path test/test_files.py::MoveTest::test_link_does_not_depart test/test_files.py::MoveTest::test_move_already_at_destination test/test_files.py::MoveTest::test_move_arrives test/test_files.py::MoveTest::test_move_avoids_collision_with_existing_file test/test_files.py::MoveTest::test_move_changes_path test/test_files.py::MoveTest::test_move_departs test/test_files.py::MoveTest::test_move_file_with_colon test/test_files.py::MoveTest::test_move_file_with_multiple_colons test/test_files.py::MoveTest::test_move_in_lib_prunes_empty_dir test/test_files.py::MoveTest::test_move_to_custom_dir test/test_files.py::MoveTest::test_read_only_file_copied_writable test/test_files.py::HelperTest::test_ancestry_works_on_dir test/test_files.py::HelperTest::test_ancestry_works_on_file test/test_files.py::HelperTest::test_ancestry_works_on_relative test/test_files.py::HelperTest::test_components_works_on_dir test/test_files.py::HelperTest::test_components_works_on_file test/test_files.py::HelperTest::test_components_works_on_relative test/test_files.py::HelperTest::test_forward_slash test/test_files.py::AlbumFileTest::test_albuminfo_move_changes_paths test/test_files.py::AlbumFileTest::test_albuminfo_move_copies_file test/test_files.py::AlbumFileTest::test_albuminfo_move_moves_file test/test_files.py::AlbumFileTest::test_albuminfo_move_to_custom_dir test/test_files.py::ArtFileTest::test_art_deleted_when_items_deleted test/test_files.py::ArtFileTest::test_art_moves_with_album test/test_files.py::ArtFileTest::test_art_moves_with_album_to_custom_dir test/test_files.py::ArtFileTest::test_move_last_file_moves_albumart test/test_files.py::ArtFileTest::test_move_not_last_file_does_not_move_albumart test/test_files.py::ArtFileTest::test_setart_copies_image test/test_files.py::ArtFileTest::test_setart_sets_permissions test/test_files.py::ArtFileTest::test_setart_to_conflicting_file_gets_new_path test/test_files.py::ArtFileTest::test_setart_to_existing_art_works test/test_files.py::ArtFileTest::test_setart_to_existing_but_unset_art_works test/test_files.py::RemoveTest::test_removing_item_outside_of_library_deletes_nothing test/test_files.py::RemoveTest::test_removing_last_item_in_album_with_albumart_prunes_dir test/test_files.py::RemoveTest::test_removing_last_item_preserves_library_dir test/test_files.py::RemoveTest::test_removing_last_item_preserves_nonempty_dir test/test_files.py::RemoveTest::test_removing_last_item_prunes_dir_with_blacklisted_file test/test_files.py::RemoveTest::test_removing_last_item_prunes_empty_dir test/test_files.py::RemoveTest::test_removing_without_delete_leaves_file test/test_files.py::SoftRemoveTest::test_soft_remove_deletes_file test/test_files.py::SoftRemoveTest::test_soft_remove_silent_on_no_file test/test_files.py::SafeMoveCopyTest::test_self_copy test/test_files.py::SafeMoveCopyTest::test_self_move test/test_files.py::SafeMoveCopyTest::test_successful_copy test/test_files.py::SafeMoveCopyTest::test_successful_move test/test_files.py::SafeMoveCopyTest::test_unsuccessful_copy test/test_files.py::SafeMoveCopyTest::test_unsuccessful_move test/test_files.py::PruneTest::test_prune_existent_directory test/test_files.py::PruneTest::test_prune_nonexistent_directory test/test_files.py::WalkTest::test_ignore_directory test/test_files.py::WalkTest::test_ignore_everything test/test_files.py::WalkTest::test_ignore_file test/test_files.py::WalkTest::test_sorted_files test/test_files.py::UniquePathTest::test_conflicting_file_appends_1 test/test_files.py::UniquePathTest::test_conflicting_file_appends_higher_number test/test_files.py::UniquePathTest::test_conflicting_file_with_number_increases_number test/test_files.py::UniquePathTest::test_new_file_unchanged test/test_files.py::MkDirAllTest::test_child_does_not_exist test/test_files.py::MkDirAllTest::test_parent_exists
beets/config_default.yaml docs/changelog.rst beets/dbcore/db.py
beets/config_default.yaml docs/changelog.rst beets/dbcore/db.py