Engineering Practice
When the SDK and the Server Disagree, Believe the Server
A vendor's official client library is an abstraction over the real system, and abstractions can be wrong — so when the SDK builds a request the server flatly rejects, stop debugging your code and drop to the interface the server actually documents.
- Engineering Practice
- APIs
- Debugging
- Tooling
I was automating a device through its vendor-provided client library — the official, blessed SDK, the one the docs point you to. A call that should have been routine kept failing: the server rejected the request outright with an “unsupported media type” error, before it even looked at what I was asking for. I spent a while assuming I was holding the SDK wrong. I wasn’t. The SDK was building a request in a shape that particular firmware version no longer accepted. The library and the device disagreed about reality, and I’d been trusting the wrong one.
An SDK is a claim about the server, not the server
It’s easy to treat a vendor’s client library as authoritative — it’s their code for their product, so surely it knows how to talk to it. But an SDK is an abstraction layer built at some point in time against some version of the server, and the server moves. Firmware updates, API versions bump, accepted formats change. The SDK encodes assumptions about how the server behaves, and any of those assumptions can go stale. When it does, the library confidently constructs a request the server no longer likes, and you get a failure that looks like your mistake because it came out of your call to their code.
A client library is a snapshot of how the server used to behave. The server is how it behaves now. When they disagree, the server is not the one that’s wrong.
The server’s rejection is ground truth
The thing that broke my “I must be using it wrong” spiral was reading the server’s response instead of my code. The rejection was specific and early — the server refused the request’s format before processing it at all. That’s not ambiguous. It’s the server telling you, in its own voice, “I will not accept what you sent,” and it doesn’t matter that a trusted library sent it. The server is the ground truth about what the server accepts. The SDK is just a hopeful intermediary.
This is the same instinct as trusting the system of record over the ticket: when a convenient abstraction and the actual system disagree, believe the system. A ticket can be confidently wrong about infrastructure; an SDK can be confidently wrong about a server. Both are secondhand claims, and reality overrules them.
Drop to the layer the server actually documents
Once you accept the SDK might be the problem, the move is to bypass it and speak to the server the way the server documents — the raw interface underneath. For me that meant setting the library aside and driving the plain API and command-line interface directly, watching exactly what request went out and exactly what came back. That’s where the real answer lived: the lower-level, documented path worked fine, which proved the capability was there and the library’s construction of the request was the defect.
Dropping a layer is diagnostic gold for two reasons. It tells you whether the problem is the server (the raw call also fails → it’s genuinely unsupported) or the abstraction (the raw call works → the SDK is building it wrong). And it usually hands you a working path right now, while the SDK issue gets sorted out later. The abstraction exists for convenience; when convenience breaks, the documented interface is your escape hatch, and it’s often the more honest place to have been talking to anyway.
Keep a way to see the actual bytes
The prerequisite for all of this is being able to observe the real exchange — the actual request the SDK emitted and the actual response the server gave. If the library hides that behind a friendly wrapper and a generic exception, you’re debugging blind, arguing with an abstraction you can’t see through. So I value tooling that lets me watch the wire: verbose modes, request logging, a proxy, or just reconstructing the raw call by hand. The moment I can see what actually went out and what actually came back, “the SDK and the server disagree” stops being a mystery and becomes a diff I can read.
The habit that sticks: when a trusted client library fails against a live system, don’t assume the library is right and you’re wrong. Ask the server directly, in its own documented language, and believe what it says. Abstractions are conveniences, not authorities — and the server is always the authority on itself. If you’ve been sent in circles by an SDK that was quietly out of step with its own product, I’d like to hear the story.