Caption .txt sidecar files: format, naming and bulk editing
The short answer
A caption sidecar is a plain-text file that sits next to an image and shares its base name: forest_01.png pairs with forest_01.txt. For tag captions, write one line of tags separated by a comma and a space, save it as UTF-8, and use one style (spaces or underscores) for the whole dataset. ImgTagger can load images and their .txt files together, edit tags across the batch, and export a ZIP of updated captions, with or without the images.
Sidecar caption files are the most common way to store training captions. Being plain text, they open in any editor and are easy to script. They are also unforgiving: one wrong character in a file name and the caption is silently ignored.
This page covers the format rules, how the ImgTagger tag editor reads and writes these files, and how to fix the mistakes that most often break a dataset.
What a sidecar file is
A sidecar is a separate file that carries information about another file. In a dataset, the image holds the pixels, a .txt beside it holds the caption, and the two are paired purely by name:
forest_01.png forest_01.txt
forest_02.webp forest_02.txt
A tag caption is a single line:
no humans, scenery, forest, tree, sky, day, outdoors
Trainers such as kohya-ss sd-scripts find a caption by taking the image's path, dropping its extension and adding the caption extension. That is the whole lookup, which is why naming matters so much.
Naming rules
- Same base name, different extension. The base name is everything before the last dot, so
forest_01.pngneedsforest_01.txt. - One caption per base name. If
cat_01.pngandcat_01.jpgshare a folder, both point atcat_01.txt. Give every image a unique base name. - Match the case. Windows and macOS usually ignore case in file names, but Linux doesn't, and many training machines run Linux. There,
Cat_01.pngwon't findcat_01.txt.
Here is how ImgTagger handles names when you drop files in:
- A .txt is matched to the image with the same base name, ignoring case, so
Cat_01.TXTstill pairs withcat_01.png. On export, the .txt is named exactly after the image, which fixes case mismatches. - A .txt with no matching image shows a "No matching image found" warning, and its tags are discarded. Drop it again once the image is loaded.
- Dropping a .txt for an image that is already loaded adds its tags to the existing ones rather than replacing them.
- Dropping an image with the same file name as one already loaded replaces the picture and keeps its tags. Handy after you crop or retouch a file, as long as the name and extension stay the same.
- Images are identified by file name alone. Two different files called
001.pngfrom different folders can't both be loaded: the second replaces the first and inherits its tags.
Separators and spacing
Tag captions separate tags with commas. ImgTagger writes a comma followed by a space (smile, upper body, outdoors), which is the most common style. When it reads a file, it splits on commas and trims the spaces, so smile,upper body and smile , upper body load the same way.
Because commas are the separator, a tag can't contain one (none of the WD14 tags do). A sentence inside a tag caption also gets split at its commas: ImgTagger would show "a cat by the window" and "looking outside" as two tags, and trainers that shuffle tags would shuffle the fragments.
Keep each caption on one line. ImgTagger splits only on commas, so a file with one tag per line and no commas loads as one long tag. kohya-ss sd-scripts reads only the first line of a caption file unless its wildcard option is turned on.
Spaces or underscores
Danbooru tags use underscores (long_hair) because a space separates tags on the site. In comma-separated captions spaces are safe, and ImgTagger outputs long hair. Both styles are in wide use. What breaks things is mixing them: to your trainer, and in ImgTagger's Filter panel, long hair and long_hair are two different tags.
Mixing usually happens when you load an older dataset that used underscores and then auto-tag it. The old long_hair stays, and the tagger adds long hair beside it. To stay consistent:
- If you work in ImgTagger, use spaces everywhere, including hand-typed tags and the "Always add these tags first" box.
- Convert an underscore dataset before adding new tags. ImgTagger has no find-and-replace, so use an editor that can replace across many files, such as VS Code, to swap
_for a space. - Check what a blind replace would break: a trigger word such as
mira_oc, and emoticon tags such as^_^and>_<, where the underscore is part of the face. ImgTagger leaves emoticon tags like these untouched in its own output.
Encoding and line endings
Save caption files as plain UTF-8. It is what most trainers expect: kohya-ss sd-scripts reads captions as UTF-8 and stops with an error on a file it can't decode. If your editor offers both "UTF-8" and "UTF-8 with BOM", pick plain UTF-8. Some scripts read the invisible byte-order mark as part of the first tag, which is usually your trigger word.
ImgTagger writes UTF-8 without a BOM, as one line with no line break at the end. When it reads a file it drops any BOM, so loading a dataset and exporting it again cleans that up. A file saved in an older encoding, such as Shift-JIS, shows replacement characters (�) in the tag editor. Re-save it as UTF-8 in an editor that can detect the original encoding, then load it again.
Line endings (Windows CRLF or Unix LF) don't matter for a one-line file. A trailing line break is harmless in ImgTagger and in sd-scripts, since both trim whitespace. Just avoid extra lines.
Loading an existing dataset into ImgTagger
- Drag the whole dataset folder onto the tagger page. Subfolders are included, and hidden system files such as
.DS_Storeare skipped. You can also select the images and .txt files themselves and drag them in, or click the big drop box and pick them in the file dialog. ZIP files aren't read, so unzip them first. - ImgTagger reads every caption before it loads the images, so each image picks up its caption however large the batch is.
- Open the Filter panel and look for the ∅ untagged entry. Images listed there that should have captions didn't match their files, so check the warnings and the naming mistakes below.
Bulk editing: add, prepend and remove
The tagger's side panels change many captions at once. Most actions come in a "Selected" and an "All" version. "Selected" means images that are checked and visible under the current filter, so the Filter panel is how you target a subset.
- Add Tags adds one or more comma-separated tags in Append (end) or Prepend (start) mode. Images that already have a tag are skipped. In Prepend mode, they go to the front in the order you typed them.
- Remove Tags removes whole tags, ignoring case and treating underscores and spaces alike, so
long_hairalso removeslong hair. The same panel has buttons to clear all tags. - Filter panel lists every tag with its count. Each tag's trash button deletes that exact spelling from every image in one click.
To rename a tag, use four steps: filter by the old tag in Any mode, click Select All, add the new tag to the selected images, then remove the old tag from all images. The new tag lands at the end (or the start, in Prepend mode) rather than where the old one was.
Exporting: tags only or images and tags
Click Download and choose what goes into the ZIP. ImgTagger remembers your choices for next time.
| Include | Selection | ZIP name | Use it when |
|---|---|---|---|
| .txt tag files | All Items | tags.zip | The images are already in place. Unzip over the folder to replace the old captions. |
| Images and .txt | All Items | images_tags.zip | You want a complete, self-contained copy. |
| .txt tag files | Selected Only | selected_tags.zip | You edited a subset and only want those captions. |
| Images and .txt | Selected Only | selected_images_tags.zip | You are splitting off a smaller dataset, such as every image that matches a filter. |
The ZIP is flat, with no subfolders, and every caption is named after its image. An image with no tags still gets a .txt file, which is empty.
Common mistakes and how to fix them
Double extensions (image.png.txt)
Here the base name is image.png rather than image. Trainers looking for image.txt won't find it, and ImgTagger warns that there is no matching image. It usually comes from a script that added .txt to the whole file name, or from Windows hiding known extensions so you never saw the real name.
Fix: first make extensions visible (Windows 11 File Explorer: View → Show → File name extensions; macOS Finder: Settings → Advanced → Show all filename extensions). Then rename in bulk. In a macOS or Linux terminal, inside the folder:
for f in *.png.txt; do mv "$f" "${f%.png.txt}.txt"; done
In Windows PowerShell:
Get-ChildItem *.png.txt | Rename-Item -NewName { $_.Name.Replace('.png.txt', '.txt') }
Repeat with .jpg or .webp in place of .png as needed.
Hidden system files
Operating systems leave small hidden files in folders. macOS adds .DS_Store, plus ._ files on drives formatted for Windows and on network shares (and a __MACOSX folder inside ZIPs made with Finder). Windows adds Thumbs.db and desktop.ini. Some dataset tools try to read these as images or captions and fail, and in ImgTagger a ._name.txt file triggers a "No matching image" warning.
Fix: an ImgTagger export only contains images and the captions it writes, so exporting a fresh copy leaves them behind. (Our dataset cleanup workflow covers the rest of the tidy-up.) To clean a folder in place on macOS or Linux:
find . -name '._*' -delete
find . -name '.DS_Store' -delete
On Windows, turn on hidden items in File Explorer's View menu and delete Thumbs.db and desktop.ini from the dataset folder.
Names that no longer match after renaming
Batch-renaming the images but not the captions orphans every caption. In ImgTagger you will see a run of "No matching image" warnings and a large ∅ untagged count.
Fix: undo the rename if you still can (File Explorer and Finder both can, straight away), then rename images and captions together. The safest order is to rename first and caption second. If a captioned set must be renamed, this loop for macOS, Linux, Git Bash or WSL gives each image and its caption the same new name. Run it on a copy, and choose a prefix no current file uses:
i=1
for img in *.png *.jpg *.webp; do
[ -e "$img" ] || continue
base="${img%.*}"; ext="${img##*.}"
new=$(printf "forest_%03d" "$i")
mv "$img" "$new.$ext"
[ -f "$base.txt" ] && mv "$base.txt" "$new.txt"
i=$((i+1))
done
Duplicate tags
Duplicates come in two kinds. Exact repeats, such as smile, smile, usually come from merging captions from two sources or from typing in the tag editor. Near-duplicates are one tag in two spellings: long hair and long_hair, or Smile and smile.
Fix: exact repeats disappear when you load the files into ImgTagger and export them again. For near-duplicates, search the Filter panel (searching long shows both spellings together), Filter by the variant in Any mode, click Select All, and add the spelling you keep to the selected images. Then click the variant's trash button in the Filter panel. (Remove Tags would delete both spellings, since it ignores case and underscores.) For tags that are spelled right but not worth keeping, see the tag pruning guide.
Empty caption files
An empty .txt is usually an image you forgot to tag. ImgTagger exports one for every untagged image, and kohya-ss sd-scripts stops with a "caption file is empty" error when it finds one. Fix: before exporting, check the ∅ untagged entry in the Filter panel, then tag those images or leave them out of the export. It is one of the checks in our LoRA captioning workflow.
Run WD14 tagger models on a whole folder, then search, filter and bulk-edit the tags before exporting .txt caption files. It runs in your browser. Open the Batch WD14 Tagger & Editor.
Frequently asked questions
Can I use .caption files instead of .txt?
ImgTagger reads and writes .txt files only. kohya-ss sd-scripts looks for .caption by default, but the simplest fix is to tell it to read .txt with --caption_extension=".txt". If another tool insists on .caption, change the extensions in bulk with a file renamer after you export.
Does exporting change my image files?
No. ImgTagger puts your original files into the ZIP byte for byte, with no resizing, recompression or metadata changes. The tagger works on a resized copy in memory and never writes it out.
Can I keep my captions in one JSON file instead of many .txt files?
The fine-tuning method in kohya-ss sd-scripts reads a JSON metadata file, and the repository includes a script, merge_dd_tags_to_metadata.py, that builds one from a folder of .txt tag files. ImgTagger itself only works with .txt sidecars, so caption in ImgTagger first and merge afterwards.
Can ImgTagger keep my subfolders when it exports?
No. The ZIP is always flat. If your dataset is split across folders, such as one folder per concept, load and export one folder at a time.