Morning email arrives but ~/Sync/ED/.doc-sync-log/YYYY-MM-DD.md is ~300 bytes containing one of:
Failed to authenticate. API Error: 401 The socket connection was closed unexpectedly...Failed to authenticate. API Error: 401 <html><head><title>502 Bad Gateway</title>...cloudflare</html>- The Gotify alert:
โ ๏ธ Doc-Sync YYYY-MM-DD โ AUTH FAILED
The CLI mis-reports the real cause as “401” regardless of the underlying issue. The most likely cause is a corrupted API key file, not an actual auth-server problem.
# Inspect the key file โ look for any garbage prefix
head -c 25 ~/.config/anthropic-api-key
echo
# Check file size โ a clean key is exactly 108 bytes (no trailing newline)
wc -c ~/.config/anthropic-api-key
If the file starts with anything other than sk-ant-api03-, it’s corrupted. The historical bug was a literal -n prefix from a manual echo -n "$KEY" > file in a shell where -n was printed instead of treated as a flag.
Test the key directly against the API:
KEY=$(cat ~/.config/anthropic-api-key)
curl -s -o /dev/null -w 'HTTP %{http_code}\n' \
-H "x-api-key: $KEY" \
-H 'anthropic-version: 2023-06-01' \
https://api.anthropic.com/v1/models
HTTP 200 = key works. HTTP 401 = key is bad.
Rewrite the file cleanly with printf (which doesn’t have the -n ambiguity):
# Back up the broken version first
cp ~/.config/anthropic-api-key ~/.config/anthropic-api-key.bak.$(date +%Y%m%d-%H%M%S)
# Get the key from your password manager / wherever it lives, then:
printf '%s' 'sk-ant-api03-...' > ~/.config/anthropic-api-key
chmod 600 ~/.config/anthropic-api-key
# Verify
ls -la ~/.config/anthropic-api-key # should show 108 bytes, mode 600
head -c 25 ~/.config/anthropic-api-key # should start with sk-ant-api03-
Never use echo -n to write the key โ different shells handle -n differently and some write it as a literal prefix.
Run doc-sync manually with yesterday’s date:
~/Sync/ED/skills/doc-sync/scripts/run.sh
tail -30 ~/Sync/ED/.doc-sync-log/.last-run.log
You should see Auth precheck OK (HTTP 200) in the log and the report should be 5โ20 KB (not 300 bytes).
As of 2026-05-25, run.sh has two defenses:
- Strip on load โ
sed -E 's/^-n[[:space:]]+//; s/[[:space:]]+$//'removes a stray-nprefix and any trailing whitespace before using the key. - Step 0 fail-fast precheck โ single
/v1/modelsrequest with--max-time 15. If it returns non-200, writes the actual cause to the day’s report, Gotify-alerts at priority 8, and exits 1 instead of burning 5+ minutes on a 1 MB prompt.
So the failure mode going forward should be a clear error inside 15 seconds, not a silent stub at 3 AM.