Most retry logic retries the prompt. Effective triage retries only the layer that failed.
A free coding model endpoint fails in layers:
- the request may be invalid
- the transport may time out
- the server may be rate limited
- the model may truncate or return output that breaks your parser
If your client retries all of these the same way, you burn free quota on deterministic bugs.
The fix is small: classify the failure, then retry or repair.
Why blind retry fails
A typical client looks like this:
try:
response = call_free_model(prompt)
except Exception:
time.sleep(2)
response = call_free_model(prompt)
This retries a malformed prompt. It retries a max-token truncation. It treats every failure as transient.
Free hosted endpoints make this worse:
-
429means rate limit, not model problem -
502/503mean overload, not model problem -
200with truncated text anddone: falsemeans the max token limit was hit -
400with a validation body means the request is bad and will never succeed
Retrying a 400 is not persistence. It is a bug.
Classify before you retry
Capture four fields on every failed call:
- HTTP status
- exception type
- response body
- whether the model marked completion
Use a tiny classifier.
import time
import requests
def valid_output(body: dict) -> bool:
text = body.get('text', '')
return bool(text.strip())
def classify_failure(status, error, body, token_limit):
if status == 400:
return {
'class': 'request',
'action': 'fix_request',
'retry': False,
}
if status in (429, 502, 503, 504) or isinstance(error, requests.Timeout):
return {
'class': 'transport_or_quota',
'action': 'backoff_once',
'retry': True,
}
if status == 200:
text = body.get('text', '')
done = body.get('done', True)
if len(text) < token_limit * 0.8 and not done:
return {
'class': 'model_truncation',
'action': 'lower_prompt_or_raise_limit',
'retry': False,
}
if not valid_output(body):
return {
'class': 'model_output',
'action': 'reparse_or_rerun',
'retry': False,
}
return {
'class': 'unknown',
'action': 'log_and_stop',
'retry': False,
}
Append each result to a dict log. That gives you a failure history instead of a wall of exceptions.
{'ts': '2026-08-12T09:14:02Z', 'class': 'transport_or_quota', 'status': 429, 'retry': True}
{'ts': '2026-08-12T09:14:05Z', 'class': 'request', 'status': 400, 'retry': False, 'detail': 'unknown field'}
{'ts': '2026-08-12T09:14:11Z', 'class': 'model_truncation', 'status': 200, 'retry': False, 'token_limit': 512}
One look at this log tells you which fixes are worth doing.
Use a second endpoint to split transport from model
The classifier cannot tell some failures apart by itself.
A 200 with odd output may be:
- model quality
- a hidden prompt limit
- a server-specific parser quirk
The cheapest split is to replay the same prompt against a second endpoint and compare the class, not the score.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I use MonkeyCode's free model access and free server option as that second lane. Those two availability claims are operator-supplied, so I verify them like any endpoint: status codes, latency, and token behavior before relying on the comparison.
The replay is a narrow diagnostic, not a benchmark:
def compare_triage(prompt, primary, secondary):
results = {}
for name, endpoint in (('primary', primary), ('secondary', secondary)):
status, error, body = call_endpoint(endpoint, prompt)
results[name] = classify_failure(status, error, body, endpoint['token_limit'])
return results
Use the result as a decision table:
| Primary class | Secondary class | Meaning | Action |
|---|---|---|---|
request |
request |
The prompt is invalid | Fix the request |
transport_or_quota |
200 valid |
Primary host is overloaded | Retry primary or switch host |
200 invalid |
200 valid |
Primary parser limit differs | Adjust token or parser |
200 invalid |
200 invalid |
Model quality issue | Change prompt, not endpoint |
The point is not to chase the best model. It is to stop retrying failures that will not change.
Limits
- Status codes are not always reliable. Some endpoints return
200for failed generations. - The token threshold is heuristic. A short valid answer can look like truncation.
- Two endpoints can differ in model version without telling you. The comparison still has noise.
- A free server can be rate limited too. Keep the replay budget small.
- This taxonomy improves triage. It does not measure whether the generated code is correct.
Who should skip this
Skip the second-endpoint replay if:
- You handle regulated or private prompt data. Route that to an approved environment.
- You need hard latency SLAs. Backoff and diagnostics add milliseconds you may not have.
- You expect guaranteed capacity. Free tiers change; treat them as variable.
- You already have structured server logs. Add the classifier to your existing pipeline instead of running a new script.
Keep the classifier next to the client, not behind a dashboard. The value drops if the retry decision happens after you have already retried three times.
Run the checker on a sandbox prompt before pushing it into CI. Every classifier needs its own failure log.
Top comments (0)