Key points
- The text comes from the client, so reproduce the call with curl to see the real error body and status.
- HTTP 429 with code rate_limited or concurrency is the most common cause on a busy account, and a stream keeps its slot until it ends.
- Check base URL, key, model ID uncensored, body size (8 MB) and total tokens (100,000) before anything else.
- Retry only 429 and network drops, wait for Retry-After, and keep concurrency under the key limit.
What the message actually tells you
If you searched for unknown error from llm stream learn more, you are probably looking at a toast or a red line in a chat front end, an editor plugin or an agent framework. The wording comes from that client. It means the app asked for a streamed answer, something went wrong before or during the stream, and the app did not know how to describe it. The API itself reports problems with an HTTP status and a JSON error body, and the client has thrown that detail away.
That is good news, because it means you can get the detail back. Send the same request outside the client, read the status line and the body, and the cause is usually obvious within one or two attempts. The rest of this page goes through the causes in the order they tend to occur on this API, then gives you a small streaming client that prints the real error instead of hiding it.
One thing to keep in mind from the start: a streamed request has two failure points. It can fail before the first byte, when the server rejects the request (bad key, bad URL, a limit). Or it can fail in the middle, when the connection drops after some text has already arrived. The fixes differ, so note which of the two you see. If the answer never starts, look at sections two to four. If it starts and then stops, look at the section on proxies and timeouts.
Check the base URL, key and model first
Most stream errors on a fresh setup come from three small mistakes. They take a minute to rule out.
- Base URL. It must be
https://api.uncensoredllmhub.com/v1, including/v1and without a trailing/chat/completions. Clients append the path themselves. If you paste the full endpoint into a field that expects a base, the client requests a path that does not exist and reports a vague failure. - Key. The header is
Authorization: Bearer YOUR_KEY. A stray space, a newline copied with the key, or a key from another service all fail the same way. Create or copy the key again from the API key page if you are unsure. - Model ID. The only model is
uncensored. Clients that offer a dropdown often pre-fill a name from another provider. Type the ID by hand, and callGET /v1/modelsif you want to confirm what the server lists.
A quick way to test all three at once is the models call. It does not stream, so it separates a connection or key problem from a streaming problem:
curl -i https://api.uncensoredllmhub.com/v1/models -H "Authorization: Bearer $API_KEY"
If this returns a normal list, the URL and key are fine and you can stop suspecting them. If it fails, fix that before touching anything about streaming. Also check that your account has credit: the API sends your remaining balance in the X-Balance-USD header on responses, which is handy when a key suddenly stops working after heavy use. The pricing page explains top-ups, and new accounts can claim a $0.50 trial from the key page.
Rate limits and the stream that holds its slot
The next most common cause is HTTP 429. There are three flavours, and your client will fold all of them into the same unhelpful message.
| Signal | Meaning | What to do |
|---|---|---|
429, code rate_limited | Too many requests per minute for the key | Slow down; a full key allows 300 per minute, a trial key 60 |
429, code concurrency | Too many requests running at once | Queue them; a full key allows 8 at once, a trial key 2 |
429 with Retry-After: 5 | Temporary overload on the service side | Wait the 5 seconds, then retry |
The concurrency limit is the one that surprises people, because of how streaming works. A streamed request occupies its slot until the stream ends, not until the first token arrives. If your app opens a stream for every message and never closes the old one, for example because the user navigated away and the UI forgot to cancel the request, those slots stay taken. On a trial key with a limit of 2, two abandoned streams are enough to make the third message fail.
The symptoms are consistent. The first few messages work, then errors start, then everything works again after a short wait. If that matches, abort streams you no longer need (an AbortController in the browser, closing the response in a server), and cap the number of parallel calls in your own code. Agents are the usual offenders: a planner that fires ten tool calls at once will hit the limit on a trial key immediately.
The response headers help you see where you stand. X-RateLimit-Limit-Requests and X-RateLimit-Limit-Concurrency show the limits that apply to the key you used, and X-Request-Id identifies the call if you need to ask support about it.
Request size and context length
A request can also be refused for being too big. Two numbers matter. The body can be at most 8 MB, and the prompt plus the completion must fit in a 100,000 token context window. Chat front ends that resend the whole conversation on every turn hit these limits slowly and then all at once: the chat works for an hour, then one long message or a pasted file tips it over, and every later turn fails because the history is still in the request.
If the error starts after a long session, test with a short new conversation. If that works, the history is the cause. Trim it by dropping old turns, summarising them into one message, or starting a fresh chat. Large pasted documents are the other trigger. A text file of a few hundred kilobytes is fine, but base64 blobs or very large logs can reach 8 MB faster than you expect.
You do not have to set max_tokens, since the completion can use whatever is left of the window. If you do set it, make sure prompt tokens plus max_tokens stay under 100,000. A client that hard-codes a large max_tokens on top of a long history can push the sum over the window even when the prompt alone looks fine.
Proxies, buffering and timeouts
Streaming uses server-sent events: a long-lived HTTP response that delivers small chunks as they are produced. Anything between you and the API that waits for the whole response before forwarding it will break that. The result is either a long silence followed by a burst, or a timeout and the generic error.
Look for these in your own setup:
- Reverse proxies. If you route the API through nginx or a similar proxy, turn response buffering off for that location, and raise the read timeout above the longest answer you expect. Long answers from a 100,000 token window can take a while.
- Corporate or antivirus proxies. Some inspect HTTPS traffic and hold the body. Test from a phone hotspot to rule this out.
- Serverless functions. Many platforms cap execution time or buffer responses unless you opt in to streaming. If the stream dies at the same number of seconds every time, a platform time limit is the likely cause.
- Browser extensions and VPNs. Rare, but they can cut idle connections.
The clue that points here is an error in the middle of an answer, with the same cut-off time on every attempt. A rate limit would reject the request at the start, so a mid-stream cut that always happens at, say, 30 seconds is almost never a limit.
A client that shows the real error
Use this when you want the truth rather than the friendly message. It catches the OpenAI SDK exceptions, prints the status and the error body, and reads the retry hint when there is one.
import time
from openai import OpenAI, APIConnectionError, APIStatusError, RateLimitError
client = OpenAI(
base_url="https://api.uncensoredllmhub.com/v1",
api_key="YOUR_API_KEY",
)
def stream_once(messages):
stream = client.chat.completions.create(
model="uncensored",
messages=messages,
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if getattr(chunk, "usage", None):
print("\n[usage]", chunk.usage)
messages = [{"role": "user", "content": "Write two sentences about harbours."}]
for attempt in range(4):
try:
stream_once(messages)
break
except RateLimitError as e:
wait = int(e.response.headers.get("retry-after", 5))
print("429:", e.body, "- waiting", wait, "s")
time.sleep(wait)
except APIStatusError as e:
print("status", e.status_code, e.body)
print("request id:", e.response.headers.get("x-request-id"))
break
except APIConnectionError as e:
print("connection problem:", e)
time.sleep(2)
The same idea in Node, with an abort signal so abandoned streams release their slot:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.uncensoredllmhub.com/v1",
apiKey: process.env.API_KEY,
});
const controller = new AbortController();
try {
const stream = await client.chat.completions.create(
{
model: "uncensored",
messages: [{ role: "user", content: "Write two sentences about harbours." }],
stream: true,
},
{ signal: controller.signal }
);
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
} catch (err) {
console.error("status:", err.status, "body:", err.error);
}
// call controller.abort() when the user leaves the page
And a raw curl call, which removes every client from the picture. -N turns off curl's own buffering and -i prints the status line and headers, including X-Request-Id:
curl -N -i https://api.uncensoredllmhub.com/v1/chat/completions -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d '{"model":"uncensored","stream":true,"messages":[{"role":"user","content":"Say hello."}]}'
If curl streams text fine and your app does not, the API is healthy and the problem sits in the app, its proxy or its settings. If curl shows a 429 or another status, you now have the exact cause.
Retry rules that do not make things worse
Retries help with transient problems and hurt with permanent ones. A short policy keeps you out of trouble.
- Retry 429 and dropped connections. Wait for
Retry-Afterwhen it is present, otherwise back off for a few seconds and add some random jitter so parallel workers do not retry in lockstep. - Do not retry authentication failures or oversized requests. The result will not change, and each retry only adds noise.
- Limit attempts to three or four, then show the user the real error text.
- Cap your own parallelism below the key limit. A semaphore of 6 on a full key leaves room for a second tool using the same key.
- If a stream breaks halfway, keep the partial text. Resend with that text as an assistant message and ask the model to continue, rather than regenerating from scratch.
Failed or refused requests are not charged, so debugging with repeated test calls does not drain your credit. Successful calls are billed at $0.15 per 1M input tokens and $0.50 per 1M output tokens, and a partial stream that completes normally is simply a short answer. More detail on the request format is in the API documentation.
If you have done all of the above and still see the message, send the X-Request-Id of a failing call to the support page, with the time of the attempt and whether the failure came before or after the first token. That information is enough to trace what happened.
