What to Do When a Migration Target Has No Equivalent for a Sampling Parameter
9 min read · updated August 11, 2026
You changed the base URL, the request went out unchanged, and the API returned a 400 naming a parameter that has worked in production for two years. That is the good outcome. The bad outcome is a 200 with the parameter quietly discarded.
The errors, and the one that is not an error
Unsupported parameters surface in three ways, and it is worth being able to recognise all three from the response alone.
Explicit rejection by name. OpenAI-shaped APIs return a 400 with an invalid_request_error whose message names the field, in the form Unrecognized request argument supplied: top_k, with the offending field also given in the error object’s param. This is the easiest case: the field name is in the string, so you know exactly what to remove.
Schema validation rejection. APIs that validate the body against a strict schema reject unknown fields generically, with a message that names the field and complains that extra input is not permitted rather than that the argument is unrecognised. Anthropic’s Messages API is strict in this way — its documented body has no repetition-penalty field of any kind — so a forwarded frequency_penalty fails validation rather than being ignored. The exact wording of these validation messages varies with the server’s validation library and version, so match on the error type and the named field, never on the sentence.
Silent acceptance. Many OpenAI-compatible servers accept the full OpenAI field set to satisfy client libraries and implement a subset. The request succeeds, finish_reason looks normal, and your carefully tuned setting does nothing. Nothing in the response tells you. This is the failure that gets attributed to “the new model is worse” for a fortnight.
There is a cheap test for the third case, and it is worth running once per parameter per target before you migrate anything. Send the same prompt twice with the parameter at both extremes of its range — a frequency penalty at 0 and at 2, a top-k at 1 and at a large value — with everything else fixed and a prompt designed to make the effect obvious. If the two responses are indistinguishable across a handful of samples, the parameter is not being applied. That is a capability probe you can automate and re-run when the provider updates, and it does not require you to trust anybody’s compatibility table.
Classify the parameter before replacing it
Not every missing parameter needs a substitute, because they do not all do the same kind of work. Sort them into three buckets first.
- Distribution shaping.
temperature,top_p,top_k,min_pwhere it exists. These change which tokens are eligible and how sharply they are weighted. They overlap enough that one can partly stand in for another. - History-dependent adjustment. Frequency and presence penalties, repetition penalties. These depend on what has already been generated, and nothing in the first bucket can imitate them — see the penalty formula for why.
- Control and bookkeeping.
seed,n,logit_bias,stop,logprobs. These are not about the distribution’s shape at all; they are about reproducibility, sampling multiple candidates, forbidding tokens, terminating, and observability. Each has a different answer.
Substitutes, and what each one misses
Missing top_k, have top_p. Both truncate the candidate set: top-k by rank, top-p by cumulative probability mass. A low top-p approximates a low top-k on a peaked distribution and diverges badly on a flat one, where a fixed mass admits many more candidates than a fixed rank would. There is no fixed conversion. Pick a top-p by evaluating on your own outputs, not by a formula.
Missing seed. There is no substitute. Nothing in the request can make a server sample the same way twice if it does not offer the control, and even where it does, reproducibility is usually best-effort rather than a guarantee. What you can do is stop depending on it: pin the temperature at its minimum, assert on properties of the output rather than on exact strings, and treat the fingerprint of the serving configuration, where the provider exposes one, as the signal that something changed. The library’s general treatment is in handling a provider with no seed parameter.
Missing n. Issue n separate requests concurrently. The behaviour is close and the cost is not: a server-side n can share the prompt’s prefill across candidates, whereas separate calls pay for the input tokens every time. Budget for that before you fan out on a long prompt.
Missing logit_bias. If you used it to forbid tokens, move the constraint to stop sequences plus a post-generation check. If you used it to force a small answer set, use whatever constrained-decoding or schema-enforcement facility the target offers and validate the output; a bias map is a blunt instrument for that job anyway.
Missing penalties. Detect the repetition you care about after the fact and regenerate with a stronger instruction. This is the one bucket where the substitute lives outside the request.
Ranges that overlap but do not match
Worse than absence is a parameter that exists with a different domain. OpenAI’s Chat Completions API documents temperature over 0 to 2; Anthropic’s Messages API documents it over 0.0 to 1.0. Forward a 1.4 from one to the other and you get a validation error naming the field and its bound. Clamp it and the request succeeds, but a clamped 1.0 is not equivalent to the 1.4 you asked for, and the two APIs’ endpoints do not map linearly onto each other in any case because the underlying distributions differ.
So clamp, but never clamp silently. A migration adapter that quietly maps out-of-range values produces a system whose behaviour differs from its configuration with nothing in the logs to say so. Clamp, record the original value and the clamped value, and re-tune the setting against your eval set on the new provider rather than assuming the old number transferred.
Making the adapter say what it dropped
- Keep a per-target capability map in code — supported, unsupported, or supported with a different range — rather than discovering support from error responses at runtime.
- Give the adapter three behaviours per parameter and choose per parameter, not globally: forward, clamp, or drop. A drop that changes output quality is a different decision from a drop that changes nothing.
- Emit a structured record on every transformation — the parameter, the requested value, the action, the target — and count them. A counter that says a parameter is being dropped on nine per cent of traffic is how you find the service nobody remembered was setting it.
- Fail loudly rather than dropping for any parameter your team has marked as behaviour-critical. Some settings are load-bearing; the adapter should refuse the request rather than serve a subtly different model.
- Re-run the extremes probe from the first section against every target on a schedule, so a provider that adds or removes support shows up as a capability-map diff instead of a quality complaint.
The general point is that a missing parameter is a fact about the target that your code should hold explicitly. Discovering it from a 400, or worse from a user, means the knowledge lives in an incident rather than in the system.