Skip to content
<k/>
0%
Loading assets · 0s
<k/>
Loading...

Technical Journal

The 100MB \r\n Bug: A Deno Deploy & GitLab CI Drama

3 min readdenoci-cdwindowsdebugging

I recently spent an embarrassing amount of time on what turned out to be a very simple problem. I code on Windows and deploy to Linux, so I should have suspected line endings earlier. The culprit was a single invisible character: the carriage return (\r).

The Symptoms: "Failed to upload build artifact"

I was working on a Deno 2 project using the Fresh framework for my personal portfolio. My CI/CD pipeline was set up in GitLab CI, configured to run tests, build the static assets, and then push the result to Deno Deploy using the CLI.

Everything worked fine on my local Windows machine. But the moment my GitLab pipeline hit the deployment stage, it choked and threw a vague error:

Failed to upload build artifact

In Deno Deploy, this error usually means the uploaded zip exceeds the 50MB limit.

Since node_modules grows quickly with npm dependencies, it can easily exceed 100MB. I assumed the Deno CLI in my pipeline was accidentally packaging it and sending it to the edge network.

The Brute-Force Attempt

My first instinct was to just delete the folder before deploying, rather than dig into ignore rules:

deploy:
  stage: deploy
  script:
    - deno task build
    - rm -rf node_modules # Nuke it from orbit
    - deno deploy --app="my-app" --prod

The CI logs reported a 7.5MB total directory size.

The deployment still failed with the same error. A 7.5MB directory shouldn't exceed any limit. I had no idea what was going on.

The Plot Twist: Edge Builders

That's when I realized I had misunderstood how edge platforms work. When I used the Deno Deploy CLI on my Fresh project, it didn't just upload my compiled files. It uploaded source code and triggered a remote build on Deno Deploy's edge servers.

  1. My CI runner successfully deleted node_modules locally.
  2. It uploaded the 7.5MB source code to the Deno Deploy Edge Builder.
  3. The Edge Builder (running Linux) executed deno task build.
  4. This re-downloaded all npm: dependencies, re-creating a 100MB+ node_modules folder on the remote server.
  5. The Edge Builder then prepared the final package to distribute to the edge network. To decide what to exclude, it read my .denoignore file.

My .denoignore file clearly had node_modules/ listed in it. So why was the remote builder still packaging it?

The Invisible Culprit

I looked at the .denoignore file more carefully and noticed the problem: Git line endings.

Because I created the .denoignore file in VSCode on Windows, and because Git on my machine defaults to checking out files with CRLF (\r\n), the file was pushed with carriage returns.

When the Linux-based Deno Deploy Edge Builder read my .denoignore file, it split the lines by \n. It read my exclusion rule not as node_modules/, but as node_modules/\r.

Because node_modules/\r did not exactly match the generated node_modules/ directory, the rule was completely bypassed. The remote builder happily packaged the 107MB directory, hit its hard limit, and crashed, returning "Failed to upload build artifact."

The Fix

The fix was simple. I just needed to convert the file to Linux line endings (LF).

So I don't hit this again, I created a .gitattributes file at the root of my project:

* text=auto
*.denoignore text eol=lf
*.gitignore text eol=lf
deno.json text eol=lf

This tells Git to always check out these config files with LF line endings, regardless of the OS.

Conclusion

The whole debugging session took way longer than it should have, and the fix was a one-line .gitattributes entry. When a cross-platform pipeline fails in a way that makes no sense, check line endings early.

Comments