After 3 months of work, today was the day to test a brand-new invoicing module for a hardware shop.
We're building a Blazor app to charge companies based on active users and license fees.
By "we" I mean a team of contractors I'm part of.
At the beginning of every month, the CEO creates invoices and charges cards manually. Zero automation. One by one.
It takes him two or three days of work.
Yes, in the 21st century.
Today, the CEO decided to compare his manual process to the new module.
On the first attempt to charge a card, we got a "Not Found" response code from the payment gateway API.
It turns out, on Octopus Deploy, our CI/CD tool, the API url and credentials were missing.
Someone added it by clicking "Add to the List" under the Project Variables.
But they forgot to hit "Save."
Adding to the list isn't the same as saving.
Who would have known it?
Arrgggg!!!
Fifteen minutes later, after a deployment, we got the same "Not Found" on the logs.
This time, we forgot to append a "/" at the end of the API url on Octopus Deploy.
From https://acoolapi/something/rest to https://acoolapi/something/rest/.
Noticed the subtle difference?
Well, it seems .NET built-in HttpClient's SendAsync() does a simple string manipulation to build urls.
I wanted to find the source code to check my assumption, but it was too late and I'm lazy—And this post is getting long.
I guess today I learned to hit Save and append slashes on urls.
Even with AI, troubleshooting matters more than ever. Street-Smart Coding shows you a roadmap with 30 lessons—troubleshooting is one of them—to help you build future-proof skills.
Top comments (6)
It seems that HttpClient.
SendAsync()boils down to an object of HttpRequestMessage, i.e.,System.Net.Http.HttpRequest. The URL is stored as aSystem.URIobject.Now you don't have any excuse to check that string manipulation by yourself. :-P
Is it that hard for some of those objects to do it for me? I'm lazy! :P
Ha ha ha... XD
The "adding to the list isn't saving" line got me — I've lost the same twenty minutes on more than one CI tool. What I like about this post is that it treats the deploy loop honestly: change, deploy, read the log, change again. That's the actual job, and it's worth saying out loud when most posts skip to the final working config.
One thing I think gets undersold here: you compared the new module against the CEO's manual process side by side. That's a much better test than a happy-path demo, and it's why you hit the gateway's real response instead of a mock.
Happy to compare notes on CI config validation if you're still poking at this — I've run into the same class of issues with Octopus variables and base URLs.
What I don't think can be argued away is that "hit Save and append slashes" is a habit, not a fix. Habits don't survive a team of contractors, a new hire, or a Friday deploy. The gateway's 404 was the only thing catching a malformed base URL, and that's the part I'd be uncomfortable shipping with.
A cheap improvement: fail fast at startup. Validate the payment config once — URL present, ends with a slash, credentials non-empty — and refuse to boot if not. It's a few lines, it catches the missing save and the missing slash before a deploy, and it turns a habit into something the code enforces.
The line that hit me: "Adding to the list isn't the same as saving. Who would have known it?"
This is the most relatable developer story I've read all week. I think every single one of us has spent 20 minutes debugging a CI/CD pipeline only to realize we forgot to hit "Save" on an environment variable. It is a rite of passage.
And the trailing slash issue is a classic! If you ever want to look under the hood of .NET's HttpClient, it has to do with how Uri handles relative paths. If your base URL doesn't end with a slash, it treats the last segment of the URL as a file rather than a directory, completely mangling the final request path. It's the kind of thing that makes you want to scream until you finally figure it out.
You're completely right: AI can write the happy path, but it's useless when the environment variables are missing a slash. Troubleshooting is the real job. Great post!
The slash gets me from the other direction. When we move WordPress sites to Next.js, every old URL ends with a slash, and by default Next.js redirects /page/ to /page before your own redirect rule runs, so each old link takes an extra hop. Two characters, two different bugs.
"Adding to the list isn't the same as saving" should be printed on every settings screen.