SOLVED (EDITED): iCloud Sync Issues & "File Not Found" on Windows - The NFD/iCloud Filename Conflict
[Report] SOLVED: iCloud Sync Issues & "File Not Found" on Windows - The NFD/iCloud Filename Conflict (Zotero 8)
1. Environment & Setup
- OS: macOS 13.7.8 (Primary) & Windows 11 Pro 25H2 (Secondary)
- Zotero Version: 8.0.3 (x64)
- Data Directory: Local (Fresh Installation)
- Attachment Storage:** iCloud Drive via ZotMoov (Shared folder: `...\iCloudDrive\Family Share\LIBRARY\ZOTERO-REEN`)
- Key Extension: ZotMoov (1.2.26)
2. The Problem: "The 8-Second Filename Mismatch"
- Symptom:** Attachments added on Mac (with long Korean titles) were synced to iCloud, but after ~8 seconds, the filenames were forcibly truncated by the iCloud server.
- Result:** Zotero's database recorded the original long filename, while the actual file on iCloud became shorter. This caused a permanent "File Not Found" error on Windows, as the database link and physical filename no longer matched.
- Specific Trigger:** This was most severe with **Korean characters (NFD encoding)** combined with symbols like commas (`,`) or colons (`:`).
3. Root Cause: iCloud Server-Side Sanitization & NFD Encoding
- NFD Multiplier:** On macOS, Korean characters are decomposed (NFD), making each character occupy ~3 bytes. A 70-character title can exceed 200+ bytes.
- Server-Side Truncation:** If the total path (Folders + Filename) exceeds iCloud's hidden byte limit, the server forcibly "sanitizes" the filename after upload.
- Variable Threshold:** The safe limit depends on your folder depth. In my case (~4 folders deep), the "Magic Number" for filename safety was **36 characters**.
4. The Definitive Solution: Pre-emptive Truncation
Step 1: Implement the "Safe Zone" Renaming Template
I modified the Zotero 8 template to strip illegal characters and truncate the filename `before` iCloud could intervene. This ensures Zotero's DB and the physical file always share the same name.
### Template Code: [OLD]
{{ firstCreator replaceFrom=", " replaceTo=" " }} - {{ year }} - {{ title replaceFrom=" : " replaceTo=" " replaceFrom=": " replaceTo=" " replaceFrom=", " replaceTo=" " truncate="36" }}
(Note: Adjust the `truncate` value (e.g., 30 or 40) based on your specific directory depth.)*
"Conclusion New v2 for Report"
Encoding Issue: macOS NFD encoding for Korean characters consumes ~9-12 bytes per character, quickly hitting iCloud's path limit (~255 bytes).
Comparison: English characters (1 byte) are much safer.
Solution: Implemented a Conditional Renaming Template using authorsCount to dynamically adjust truncate values. This ensures filenames stay within the safe byte-limit regardless of language or author count.
The previous logic failed to prevent trailing commas because Zotero applies truncate as the final step, often re-inserting symbols at the cut boundary. To address this permanently, I integrated a boundary-anchor regex ([\s,:]+$) that specifically targets and removes punctuation and whitespace at the end of the string. This ensures that even if the title is long, the final output remains clean without relying on manual length adjustments.
### Template Code: [OLD v3]
{{ if {{ authorsCount == 1 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" truncate="22" }}{{ elseif {{ authorsCount == 2 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" truncate="16" }}{{ else }}{{ firstCreator max="1" replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" regexOpts="g" truncate="21" }}{{ endif }}
"Conclusion New v3 for Report"
When using Zotero with the system language set to Korean, the {{ firstCreator }} tag automatically appends the Korean suffix "등" for multiple authors. My previous template also had a manual suffix=" et al", resulting in redundant filenames like "Author 등 et al".
Solution:
I updated the template to remove the manual suffix and rely on Zotero's localized default. To maintain path stability for iCloud/Windows syncing, I precisely recalculated the truncate values to compensate for the removed characters, ensuring the total path length remains consistent and within limits (MAX_PATH).
Updated Code Logic:
Removed suffix=" et al" to prevent duplication with Zotero's auto-generated "등".
Adjusted truncate for the else (3+ authors) condition from 16 to 21 to utilize the recovered character space safely.
### Template Code: [NEW v4] - LATEST VERSION
Why use this custom naming rule?
Since HTML snapshots often have extremely long titles, they frequently trigger "Path too long" errors in Windows iCloud. My updated formula intelligently truncates the title based on the number of authors while ensuring words aren't cut in half:
What this does:
Author Count logic: Dynamically adjusts the title length (21, 15, or 20 chars) depending on how many authors are listed.
Clean Titles: Removes colons, commas, and double spaces that often cause sync issues.
Smart Truncation: The regex ^(.{0,N}[^\s.,]).*$ ensures the filename doesn't end with a space or a hanging period/comma, keeping the file path clean and valid for Windows.
{{ if {{ authorsCount == 1 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,21}[^\s.,]).*$" replaceTo="$1" }}{{ elseif {{ authorsCount == 2 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,15}[^\s.,]).*$" replaceTo="$1" }}{{ else }}{{ firstCreator max="1" replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,20}[^\s.,]).*$" replaceTo="$1" }}{{ endif }}
(Note: Adjust the `truncate` value (e.g., 30 or 40) based on your specific directory depth.)*
Step 2: Force Rename via Zotero 8 UI
Instead of relying on auto-sync, I used the **dedicated "Rename from Parent Metadata" button in the right-side Item Pane**. This forces the database to record the sanitized, short filename immediately.
5. Final Recommendations for Non-English Users
1. Sanitize Everything: Strictly avoid commas, colons, and equals signs in templates.
2. Find Your Magic Number: If your filename changes ~8 seconds after upload, your `truncate` value is too high. Decrease it until the name remains stable.
3. Keep Paths Shallow: Deeper folder structures (Family Share, etc.) require even shorter filenames to stay within the total path limit.
4. Zotero 8 Workflow: The new Item Pane buttons are more reliable for ensuring metadata-to-file consistency than legacy context menus.
---
Shared to assist the Zotero community in optimizing cross-platform workflows with multi-byte characters and cloud sync.
1. Environment & Setup
- OS: macOS 13.7.8 (Primary) & Windows 11 Pro 25H2 (Secondary)
- Zotero Version: 8.0.3 (x64)
- Data Directory: Local (Fresh Installation)
- Attachment Storage:** iCloud Drive via ZotMoov (Shared folder: `...\iCloudDrive\Family Share\LIBRARY\ZOTERO-REEN`)
- Key Extension: ZotMoov (1.2.26)
2. The Problem: "The 8-Second Filename Mismatch"
- Symptom:** Attachments added on Mac (with long Korean titles) were synced to iCloud, but after ~8 seconds, the filenames were forcibly truncated by the iCloud server.
- Result:** Zotero's database recorded the original long filename, while the actual file on iCloud became shorter. This caused a permanent "File Not Found" error on Windows, as the database link and physical filename no longer matched.
- Specific Trigger:** This was most severe with **Korean characters (NFD encoding)** combined with symbols like commas (`,`) or colons (`:`).
3. Root Cause: iCloud Server-Side Sanitization & NFD Encoding
- NFD Multiplier:** On macOS, Korean characters are decomposed (NFD), making each character occupy ~3 bytes. A 70-character title can exceed 200+ bytes.
- Server-Side Truncation:** If the total path (Folders + Filename) exceeds iCloud's hidden byte limit, the server forcibly "sanitizes" the filename after upload.
- Variable Threshold:** The safe limit depends on your folder depth. In my case (~4 folders deep), the "Magic Number" for filename safety was **36 characters**.
4. The Definitive Solution: Pre-emptive Truncation
Step 1: Implement the "Safe Zone" Renaming Template
I modified the Zotero 8 template to strip illegal characters and truncate the filename `before` iCloud could intervene. This ensures Zotero's DB and the physical file always share the same name.
### Template Code: [OLD]
{{ firstCreator replaceFrom=", " replaceTo=" " }} - {{ year }} - {{ title replaceFrom=" : " replaceTo=" " replaceFrom=": " replaceTo=" " replaceFrom=", " replaceTo=" " truncate="36" }}
(Note: Adjust the `truncate` value (e.g., 30 or 40) based on your specific directory depth.)*
"Conclusion New v2 for Report"
Encoding Issue: macOS NFD encoding for Korean characters consumes ~9-12 bytes per character, quickly hitting iCloud's path limit (~255 bytes).
Comparison: English characters (1 byte) are much safer.
Solution: Implemented a Conditional Renaming Template using authorsCount to dynamically adjust truncate values. This ensures filenames stay within the safe byte-limit regardless of language or author count.
The previous logic failed to prevent trailing commas because Zotero applies truncate as the final step, often re-inserting symbols at the cut boundary. To address this permanently, I integrated a boundary-anchor regex ([\s,:]+$) that specifically targets and removes punctuation and whitespace at the end of the string. This ensures that even if the title is long, the final output remains clean without relying on manual length adjustments.
### Template Code: [OLD v3]
{{ if {{ authorsCount == 1 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" truncate="22" }}{{ elseif {{ authorsCount == 2 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" truncate="16" }}{{ else }}{{ firstCreator max="1" replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" regexOpts="g" truncate="21" }}{{ endif }}
"Conclusion New v3 for Report"
When using Zotero with the system language set to Korean, the {{ firstCreator }} tag automatically appends the Korean suffix "등" for multiple authors. My previous template also had a manual suffix=" et al", resulting in redundant filenames like "Author 등 et al".
Solution:
I updated the template to remove the manual suffix and rely on Zotero's localized default. To maintain path stability for iCloud/Windows syncing, I precisely recalculated the truncate values to compensate for the removed characters, ensuring the total path length remains consistent and within limits (MAX_PATH).
Updated Code Logic:
Removed suffix=" et al" to prevent duplication with Zotero's auto-generated "등".
Adjusted truncate for the else (3+ authors) condition from 16 to 21 to utilize the recovered character space safely.
### Template Code: [NEW v4] - LATEST VERSION
Why use this custom naming rule?
Since HTML snapshots often have extremely long titles, they frequently trigger "Path too long" errors in Windows iCloud. My updated formula intelligently truncates the title based on the number of authors while ensuring words aren't cut in half:
What this does:
Author Count logic: Dynamically adjusts the title length (21, 15, or 20 chars) depending on how many authors are listed.
Clean Titles: Removes colons, commas, and double spaces that often cause sync issues.
Smart Truncation: The regex ^(.{0,N}[^\s.,]).*$ ensures the filename doesn't end with a space or a hanging period/comma, keeping the file path clean and valid for Windows.
{{ if {{ authorsCount == 1 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,21}[^\s.,]).*$" replaceTo="$1" }}{{ elseif {{ authorsCount == 2 }} }}{{ firstCreator replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,15}[^\s.,]).*$" replaceTo="$1" }}{{ else }}{{ firstCreator max="1" replaceFrom="," replaceTo="" regexOpts="g" }} - {{ year }} - {{ title replaceFrom="[:|,]\s*|\s{2,}" replaceTo=" " regexOpts="g" replaceFrom="^(.{0,20}[^\s.,]).*$" replaceTo="$1" }}{{ endif }}
(Note: Adjust the `truncate` value (e.g., 30 or 40) based on your specific directory depth.)*
Step 2: Force Rename via Zotero 8 UI
Instead of relying on auto-sync, I used the **dedicated "Rename from Parent Metadata" button in the right-side Item Pane**. This forces the database to record the sanitized, short filename immediately.
5. Final Recommendations for Non-English Users
1. Sanitize Everything: Strictly avoid commas, colons, and equals signs in templates.
2. Find Your Magic Number: If your filename changes ~8 seconds after upload, your `truncate` value is too high. Decrease it until the name remains stable.
3. Keep Paths Shallow: Deeper folder structures (Family Share, etc.) require even shorter filenames to stay within the total path limit.
4. Zotero 8 Workflow: The new Item Pane buttons are more reliable for ensuring metadata-to-file consistency than legacy context menus.
---
Shared to assist the Zotero community in optimizing cross-platform workflows with multi-byte characters and cloud sync.
Upgrade Storage