◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Cowork
◈ Cowork/2026-10-16Advanced

The Token-in-URL Way of Authenticating git Stopped Working in One Scheduled Task

A git clone stopped with 'could not read Username'. Two ways of putting a token in the URL behaved differently, and this is how I ended up on askpass.

Cowork47git18GitHub7authentication6scheduled tasks13

✦ Premium Article

A scheduled task that updates one of my website repositories stopped on its very first clone one day. The log held only these two lines.

fatal: could not read Username for 'https://github.com': No such device or address
fatal: expected flush after ref listing

Another task that ran earlier the same morning had pushed without trouble. What changed in between, I still can't say. What I could do was pin down why this one stopped, and this is a record of how, and of where I settled on handing git its credentials.

The short version: writing only the token in the URL (https://TOKEN@github.com/...) failed in this environment, while adding a user name (https://x-access-token:TOKEN@github.com/...) worked. And the form that works has its own problem: the token ends up in the repository's config file.

What was actually happening

"Cannot read Username" is an odd complaint when the URL already holds a token. The message didn't seem to fit, so I looked at what git actually sent.

# Keep only the interesting lines (git itself redacts the token in this trace)
GIT_TRACE_CURL=1 GIT_TRACE_CURL_NO_DATA=1 \
  git ls-remote "https://TOKEN@github.com/OWNER/REPO.git" HEAD 2>&1 \
  | grep -E "Send header: (GET|authorization)|Recv header: HTTP|redirecting|fatal"

With the token-only form, the exchange went like this.

=> GET /OWNER/REPO.git/info/refs?service=git-upload-pack
<= HTTP/2 401
=> GET /OWNER/REPO.git/info/refs?service=git-upload-pack
=> authorization: Basic <redacted>
<= HTTP/2 200
warning: redirecting to https://github.com/OWNER/REPO.git/
<= HTTP/2 401
fatal: could not read Username for 'https://github.com'

After the first 401, the retry with credentials succeeded and returned 200. Right after that git printed "redirecting", and the third request carried no authorization header. That request got a 401, git tried to ask for a Username, and with no terminal available it gave up.

With the user-name form, the same warning appeared, but the third request did carry the header and returned 200.

URL formRequest sequenceResult
Token only401 → 200 → (warning) → 401 without authFailed
User:token401 → 200 → (warning) → 200 with authWorked

I haven't worked out why only the first form drops its credentials, or where the "redirecting" warning comes from. From the URL alone I couldn't even tell whether GitHub or something in front of it produced the redirect. What I could confirm is that the way the URL is written decides whether authentication survives the same warning. The git version was 2.34.1.

Six ways of handing over a token, side by side

Finding a form that works would have been a fine place to stop, but then I started wondering where the token ends up. The procedure I'd been following includes git remote set-url origin https://TOKEN@github.com/..., which writes the token into .git/config. So I tried each method and looked at two things: the config file, and the process list while git was running. I used a dummy token for that.

MethodWorked?Stays in config?Visible in process arguments?
Token only in the URLFailedYes, if set as a remote (same mechanism as user:token)Yes, if the URL is passed as an argument
User:token in the URLWorkedYes (and git remote -v prints it as is)Yes
http.extraHeaderWorkedNoYes (the Base64 undoes in one line)
GIT_ASKPASS (token in an environment variable)WorkedNoNo (it is in the environment)
GIT_ASKPASS (token read from a file)WorkedNoNo (not in the environment either)
credential.helper storeOnly checked that it savesYes, in a plain file (mode 600)Not measured

What surprised me was that the token-only form, the one I see most often, was the only one that failed. And the one method that both works and leaves nothing behind is the fifth row, which is also the most tedious to write.

The Base64 in extraHeader is readable. When I decoded the string I could see in the process list, it came back as x-access-token: followed by the token, unchanged. That is a rewording, not encryption, and I'd treat it as such.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦When git stops on authentication inside a scheduled task, you can read three lines of the HTTP trace and tell whether the URL form is the cause
✦You can drop a ten-line wrapper into your own setup that calls git without leaving the token in a config file or in process arguments
✦You can find, with a read-only script, which of your local repositories still carry a token inside the remote URL
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ Cowork2026-09-16
The scheduled task that only ran on days my desk was awake
Cowork scheduled tasks run remotely by default, but the moment one needs a local file or app, it runs only on your machine. Here is how the last field in the setup dialog decides that, and the three questions I now answer before creating a task.
◈ Cowork2026-09-15
What you keep the agent from walking matters more than what you hand it
Searching one connected Cowork folder for the same word gave three different answers. Here is what the default search silently left out, and how to measure a folder's scan scope once before you hand it over.
◈ Cowork2026-09-05
I verify what my Cowork memory claims instead of trusting its timestamp
Persistent memory keeps asserting whatever was true the day you wrote it. After an unattended job quietly read an empty folder for months, I stopped judging memory by its modification date and started attaching a verification step to every claim that can rot.