Qwen API Errors, finish_reason, and Troubleshooting

A Qwen API request can fail at several different layers. An HTTP error means the request itself failed. A provider error code explains the specific cause. A successful HTTP response can still contain an incomplete result, which is why applications must also inspect finish_reason, output fields, token usage, and tool calls.

Quick answer: In the current Qwen OpenAI-compatible Chat Completions API, the terminal finish_reason values are stop, length, and tool_calls. During streaming, finish_reason is normally null until the final completion chunk. Do not mark a response as successful based only on HTTP 200: reject truncated length output, execute and validate tool_calls, and treat a stream that ends without a terminal reason as incomplete.

This guide focuses on text-generation requests made through QwenCloud or Alibaba Cloud Model Studio using OpenAI-compatible Chat Completions or the DashScope chat interface. The Responses API, image generation, video generation, speech, realtime WebSocket, and third-party or self-hosted Qwen endpoints use additional status and error structures.

Independent verification note: Try-Qwen-AI.com is an independent resource. Exact errors can vary by region, billing plan, API protocol, model, SDK version, and provider. Always preserve the raw status, provider code, message, and request ID before changing the request.

Scope: Which Qwen API Does This Guide Cover?

InterfacePrimary completion indicatorError structure
OpenAI-compatible Chat Completionschoices[0].finish_reasonHTTP status plus an OpenAI-style error object
DashScope chat or text generationoutput.choices[].finish_reason or output.finish_reasonstatus_code, request_id, code, and message
OpenAI-compatible Responses APIresponse.status and output-item statusesResponse-level error plus HTTP errors
Asynchronous image or video APItask_status and per-output result stateTask-level and individual-output error codes
Self-hosted Qwen through vLLM or SGLangRuntime-specific OpenAI-compatible responseDepends on the server, model template, and runtime version

Do not apply the Chat Completions finish_reason rules blindly to the Responses API or an asynchronous media endpoint.

60-Second Qwen API Diagnosis

  1. Call Qwen directly. Temporarily bypass the agent framework, proxy, router, or no-code tool.
  2. Verify the API key and Base URL as one pair. Pay-as-you-go, Token Plan, Coding Plan, regions, and protocols can use different endpoints.
  3. Copy the exact Model ID from the current model catalog.
  4. Send one short text message with optional features disabled.
  5. Log the HTTP status, provider code, message, and request ID.
  6. For HTTP 200, inspect finish_reason, content, reasoning_content, and tool_calls.
  7. If streaming, confirm that a final terminal chunk arrived.
  8. Retry only transient failures. Do not retry an unchanged invalid request.

A minimal direct call separates provider problems from errors introduced by a framework that converts messages, strips fields, buffers SSE, changes the model name, or retries automatically.

Qwen API troubleshooting workflow from HTTP errors and successful responses to finish_reason validation and safe retry decisions

HTTP Status, Provider Code, and finish_reason Are Different

SignalWhat it tells youExample
HTTP statusWhether the API request succeeded at the transport and service level400, 401, 429, or 503
Provider error codeThe specific cause inside that HTTP categoryThrottling.BurstRate or ModelNotFound
Error messageHuman-readable explanation and often the invalid parameterRange of input length should be [1, ...]
Request IDThe unique identifier used for logs and support investigationA provider-generated UUID
finish_reasonWhy a successful Chat Completion stopped generatingstop, length, or tool_calls

A response can return HTTP 200 and still be unusable. For example:

  • finish_reason="length" means the output is incomplete.
  • finish_reason="tool_calls" means the application must execute a tool before a final answer exists.
  • finish_reason="stop" can result from a custom stop sequence that ended the answer earlier than expected.
  • A stream can disconnect before its terminal chunk, leaving partial text despite the initial HTTP 200.

OpenAI-Compatible vs DashScope Error Responses

OpenAI-compatible error

{
  "error": {
    "message": "Incorrect API key provided.",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_api_key"
  }
}

Your SDK normally raises an exception for a non-2xx response. Inspect the exception’s HTTP status, provider body, and request ID rather than matching only the text of the message.

DashScope-native error

{
  "status_code": 400,
  "request_id": "request-uuid",
  "code": "InvalidParameter",
  "message": "The request parameter is invalid."
}

DashScope SDK behavior also varies by language. The Python SDK exposes fields such as status_code, request_id, code, and message, while another SDK may throw an exception instead.

Normalize both formats inside your application so that every failure produces the same internal diagnostic record.

Qwen finish_reason Values Explained

ValueMeaningApplication action
stopThe model ended naturally or matched a configured stop sequenceAccept only after validating that the expected content is present.
lengthThe response reached the configured or model-supported output limitMark as truncated; do not parse or publish it as complete.
tool_callsThe model stopped to request one or more toolsValidate and execute the calls, append tool results, then call the model again.
nullGeneration is still in progress in a streaming responseContinue consuming chunks until a terminal value arrives.

The currently published QwenCloud Chat Completions values are stop, length, and tool_calls. Content moderation is reported through an API error such as DataInspectionFailed, not a documented Qwen Chat content_filter finish reason.

Qwen finish_reason values explained for stop, length, tool_calls, and null during streaming API responses

finish_reason stop

stop is the normal terminal value, but it does not automatically prove that the response satisfies your business task.

It can mean:

  • The model finished its answer naturally.
  • The model produced an end-of-sequence token.
  • The generated text matched a string supplied through the stop parameter.

When stop returns an unexpectedly short answer:

  1. Remove custom stop sequences and retry the diagnostic request.
  2. Check whether your framework adds hidden stop tokens.
  3. Inspect message.tool_calls even when using a third-party or self-hosted endpoint.
  4. Check whether the client discarded content during streaming assembly.
  5. Validate the response against the required schema or minimum content.

An application should never interpret stop as “all business requirements passed.” It means generation stopped normally, not that an invoice was complete, JSON was valid, or every requested field was present.

finish_reason length

length means the generated output reached a limit before finishing.

Possible limits include:

  • Your configured max_tokens.
  • Your configured max_completion_tokens.
  • The model’s maximum output limit.
  • A reasoning budget that consumed part of the total completion allowance.

For current supported Qwen reasoning models:

  • max_tokens limits the visible answer and is being deprecated for new integrations.
  • max_completion_tokens limits the complete generated output, including reasoning and the final answer.
  • max_completion_tokens is the safer choice when you need one total budget for a thinking model.

When you receive length:

  1. Mark the result as incomplete.
  2. Do not accept truncated JSON or code.
  3. Check the model’s documented maximum output.
  4. Reduce the requested scope or split the task into sections.
  5. Increase the limit only when the model and budget support it.
  6. Reduce unnecessary reasoning for a simple task.
  7. For prose, continue in another turn using the last complete sentence as a boundary.

Use the dedicated Qwen context and output limits guide for model-specific limits.

finish_reason tool_calls

tool_calls means the response is an instruction to your application, not the final answer.

The assistant message can contain an empty content field while providing one or more objects in tool_calls. The application must:

  1. Validate that every requested tool is allowlisted.
  2. Parse the arguments as JSON.
  3. Validate the arguments against the tool schema.
  4. Apply authorization and business rules.
  5. Execute the tool safely.
  6. Append the complete assistant message containing tool_calls to conversation history.
  7. Append one tool message for each call using the matching tool_call_id.
  8. Call Qwen again to obtain the final answer.

Tool arguments are model-generated and non-deterministic. Never execute them without validation, especially for payments, email, database writes, cloud infrastructure, file deletion, or shell commands.

finish_reason null During Streaming

null is expected in intermediate streaming chunks. It means the model has not reached a terminal condition yet.

data: {
  "choices": [
    {
      "delta": {"content": "Partial text"},
      "finish_reason": null
    }
  ]
}

The final completion chunk should carry stop, length, or tool_calls. When stream_options.include_usage is enabled, an additional final chunk can contain usage data with an empty choices array. Do not assume that every streaming chunk has choices[0].

Raw OpenAI-compatible SSE normally ends with:

data: [DONE]

The OpenAI SDK handles that marker internally, but your application still needs to verify that it saw a terminal finish reason before accepting the accumulated content.

Stream Ended Without a Final finish_reason

A stream that ends after partial text but never returns a terminal finish_reason should be treated as interrupted.

Possible causes include:

  • Client or browser connection loss.
  • Proxy idle timeout.
  • Nginx response buffering.
  • A load balancer closing a long-lived connection.
  • SDK or parser failure.
  • Provider-side stream termination.
  • A third-party or self-hosted server returning a non-standard stream.

Do not label the partial text as a successful answer. Save it for diagnostics, record the last event time, and retry only when repeating the operation is safe.

Qwen API Error-Code Quick Reference

StatusTypical causeRetry unchanged?First action
400Invalid JSON, parameter, context, modality, tool order, safety block, or billing-state errorNoRead the provider code and correct the request or account state.
401Missing, invalid, or mismatched API key or plan endpointNoVerify the key, environment variable, Base URL, and billing plan.
403No model, workspace, endpoint, or quota permissionNoCheck activation, workspace authorization, lifecycle, and free-tier mode.
404Wrong Model ID, unsupported protocol, workspace, resource, or pathNoCopy the exact supported ID and endpoint.
429Request rate, token throughput, burst protection, quota, or plan capacitySometimesInspect the specific throttling code before retrying.
500Internal model, plugin, retrieval, or service failureUsually, with limitsRetry with backoff and preserve the request ID.
503Temporary model unavailability or serving-capacity saturationYes, with limitsRetry later or use a tested fallback.
Client timeoutClient, proxy, network, or non-streaming request exceeded its timeoutOnly if safeUse streaming, adjust timeouts, and check partial execution.

The provider code matters more than the HTTP number alone. For example, two 429 responses can require completely different fixes.

Qwen API error codes quick reference showing 400, 401, 403, 404, 429, 500, and 503 errors with the best first fixes

Fix Qwen 400 Bad Request Errors

A 400 usually means the service understood the request route but rejected its contents.

Invalid JSON or missing fields

  • Remove trailing commas.
  • Close every brace and bracket.
  • Include model and messages.
  • Ensure every message has a valid role and content.
  • Use POST for the documented generation route.

Wrong content type

A plain-text model expects content to be a string. A multimodal model can accept an array containing typed text, image, or video objects. Sending a multimodal array to an incompatible text endpoint can produce an invalid-content error.

Invalid parameter range

  • Check temperature, top_p, and penalties.
  • Keep max_tokens or max_completion_tokens within the model limit.
  • Remove parameters unsupported by the selected model.
  • Do not send stream_options when stream=false.

Correct the request before retrying. Repeating the same invalid JSON five times cannot make it valid.

Context Length and Output Limit Errors

An error such as:

Range of input length should be [1, ...]

means the full input exceeds the model’s context limit.

The input can include:

  • System and user messages.
  • Conversation history.
  • Tool definitions.
  • Tool results.
  • Historical reasoning_content.
  • Image, video, or document tokens.
  1. Measure the entire request, not only the newest prompt.
  2. Remove irrelevant history.
  3. Summarize or compact older turns.
  4. Reduce tool descriptions.
  5. Start a new session when appropriate.
  6. Use a model with the required context window.

Input overflow produces a request error. Output overflow produces HTTP 200 with finish_reason="length". They are different failure modes.

Fix Qwen 401 Authentication Errors

Common 401 causes include:

  • The API key is missing.
  • The key was copied with spaces or a line break.
  • The application reads the wrong environment variable.
  • A QwenCloud, Token Plan, or Coding Plan key is paired with the wrong Base URL.
  • The key belongs to another region or service.
  • The process was not restarted after configuring the environment variable.

This is incorrect:

api_key = os.getenv("sk-your-real-key")

It asks the operating system for an environment variable literally named sk-your-real-key. Use:

api_key = os.getenv("DASHSCOPE_API_KEY")

Then set DASHSCOPE_API_KEY outside the source code and restart the terminal, IDE, container, or service.

Use the dedicated Qwen API key guide for secure configuration.

Fix Qwen 403 Access Errors

A valid API key can still lack permission to use the requested resource.

Provider codeTypical causeFix
AccessDeniedThe model requires activation or approvalActivate or request access in the correct region.
Model.AccessDeniedThe workspace lacks permission to call the standard modelGrant model-calling permission or use an authorized workspace.
Workspace.AccessDeniedThe key cannot access the workspaceCheck membership, RAM permissions, and Workspace ID.
Endpoint.AccessDeniedThe endpoint may belong to a retired modelCheck the model lifecycle and migrate to the replacement.
AllocationQuota.FreeTierOnlyFree quota ended while free-tier-only mode blocks paid callsComplete account setup or disable that mode when authorized.

Do not retry a permission failure continuously. Change the access, workspace, model, or billing configuration first.

Fix Qwen 404 Model and Endpoint Errors

A 404 can indicate more than a misspelled URL.

  • The Model ID is misspelled or uses incorrect capitalization.
  • A Hugging Face repository ID was used instead of a hosted Model Studio ID.
  • The model is unavailable in the selected region.
  • The model is not activated.
  • The model does not support the OpenAI-compatible protocol.
  • The Workspace ID or resource ID is wrong.
  • The request path contains an old or unsupported route.

The error:

Unsupported model ... for OpenAI compatibility mode.

means the model should be called through its documented DashScope-native endpoint instead.

Use the dedicated Qwen API Model IDs and Qwen Base URLs and Regions guides.

Fix Qwen 429 Rate-Limit and Quota Errors

Do not treat every 429 as one generic “too many requests” condition.

Provider codeLimit typeCorrect response
Throttling.RateQuotaRPS or RPM request frequencyReduce request frequency and concurrency.
Throttling.BurstRateTraffic increased too quicklySmooth traffic, queue requests, and use exponential backoff.
Throttling.AllocationQuotaTPS or TPM token throughputReduce tokens or concurrency, wait for the window, or request more quota.
insufficient_quotaPlan, quota, or billing capacityInspect the plan and billing state; a short retry may not help.
AllocationQuota.FreeTierOnlyFree quota exhausted under free-only modeChange the authorized billing configuration.

Alibaba Cloud also supports server-side queueing for Throttling.BurstRate through the X-DashScope-Wait-Timeout request header. The documented recommended range is 3–120 seconds. This does not increase absolute RPM or TPM limits.

A safe 429 response strategy is:

  1. Read the provider code.
  2. Queue requests instead of sending them simultaneously.
  3. Use exponential backoff with random jitter.
  4. Set a maximum retry count.
  5. Reduce input and output tokens when TPM is the constraint.
  6. Alert when sustained demand exceeds the purchased capacity.

Use the dedicated Qwen API Rate Limits guide for capacity planning.

Fix Qwen 500 and 503 Server Errors

Common transient provider conditions include:

  • InternalError
  • ModelServiceFailed
  • ModelServingError
  • ModelUnavailable

500/503-ModelServingError indicates temporary resource saturation. 503-ModelUnavailable indicates that the selected model is temporarily unavailable.

  1. Save the request ID and timestamp.
  2. Retry a limited number of times with backoff and jitter.
  3. Use a tested compatible fallback when the workload permits it.
  4. Do not retry an irreversible tool action without idempotency protection.
  5. Alert when the error continues beyond the retry window.

Timeout and Connection Errors

Current Model Studio documentation states that non-streaming model calls have a fixed maximum service timeout of approximately 300 seconds. A client, proxy, or load balancer can have a shorter timeout.

Use streaming for long responses and thinking models because it:

  • Returns content progressively.
  • Reduces the risk of a non-streaming 300-second timeout.
  • Allows the client to measure Time to First Token.
  • Makes partial progress visible.

Check all timeout layers:

  • SDK request timeout.
  • Reverse-proxy read timeout.
  • Load-balancer idle timeout.
  • Frontend fetch or serverless-function timeout.
  • Corporate proxy or firewall session timeout.

Do not simply set every timeout to unlimited. Dead connections can then consume workers and file descriptors indefinitely.

DataInspectionFailed and Moderation Errors

DataInspectionFailed means the input or generated output triggered the provider’s content-safety checks.

It can apply to:

  • Text prompts.
  • Images.
  • Audio or video.
  • Generated output.
  1. Inspect the complete request, including hidden system prompts and attachments.
  2. Rewrite the task to comply with the applicable policies.
  3. Remove unnecessary sensitive terms or media.
  4. Do not retry the identical blocked request repeatedly.
  5. If compliant content is reproducibly blocked, retain the request ID and submit a private support ticket.

This is an API error rather than a documented Chat Completions finish_reason="content_filter".

Thinking-Mode Errors

Thinking models add parameters and output fields that can break a previously working integration.

Error or symptomCauseFix
parameter.enable_thinking only support stream callThe selected model supports thinking only with streamingSet stream=true or choose a model supporting non-streaming thinking.
result_format must be messageDashScope thinking mode requires the message response formatSet result_format="message".
Model does not support enable_thinkingThe parameter is unsupportedRemove it or choose a compatible model.
Reasoning appears as the final answerThe client concatenated reasoning_content and contentStore and display the fields separately.
Non-streaming parser failsStreaming iteration code was reused for a complete response objectRead completion.choices[0].message directly.
Historical thinking failsPreserved reasoning was trimmed, rewritten, reordered, or put in contentPass supported historical reasoning_content exactly as documented.

When stream=false, do not send stream_options. When using the Python OpenAI SDK, Qwen-specific parameters such as enable_thinking are commonly passed through extra_body.

Structured-Output and JSON Errors

Structured output can fail even when the HTTP request succeeds.

JSON Mode requires an explicit instruction

When using response_format={"type":"json_object"}, explicitly tell the model to return JSON. Otherwise, the API can reject the request.

Truncated JSON

If finish_reason="length", the JSON may end inside a string, object, or array. Never repair it silently and treat it as model-confirmed data.

The official structured-output guidance recommends avoiding an unnecessary max_tokens cap because it can truncate the JSON. Reduce the schema or task scope when the output is too large.

Schema validation still matters

  • Parse the JSON.
  • Validate it against the expected schema.
  • Reject missing required fields.
  • Validate enums, numbers, dates, and identifiers.
  • Do not execute downstream actions from unvalidated output.

Use the dedicated Qwen Structured Output and JSON Mode guide for complete examples.

Function-Calling and Tool-Message Errors

A common request-order error is:

messages with role "tool" must be a response to a preceding message with "tool_calls"

The correct sequence is:

  1. User message.
  2. Assistant message containing tool_calls.
  3. Tool message containing the result and matching tool_call_id.
  4. Second model request.

Do not send only the tool result while omitting the assistant tool-call message.

Also check:

  • The tool name exists in the submitted tool list.
  • The arguments string is valid JSON.
  • The arguments match the declared schema.
  • Every tool response uses the correct ID.
  • The model supports Function Calling.
  • The framework did not strip tool_calls during serialization.

Use the dedicated Qwen Function Calling and Tool Use guide.

Streaming and SSE Troubleshooting

A reliable streaming client must handle more than text deltas.

  • Intermediate chunks with finish_reason=null.
  • Reasoning deltas in reasoning_content.
  • Tool-call argument deltas.
  • A terminal finish-reason chunk.
  • An optional usage-only chunk with no choices.
  • The final SSE completion marker.
  • Network disconnects and idle timeouts.

If Nginx proxies the response, its default buffering can prevent real-time delivery. A typical streaming location requires:

location /api/qwen/ {
    proxy_pass https://your-qwen-upstream;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 600s;
}

Adapt the block to your authentication, headers, TLS, and routing. Do not paste it into production without review.

Use the dedicated Qwen API Streaming with SSE guide for full frontend and backend implementations.

Responses API Does Not Use finish_reason the Same Way

The OpenAI-compatible Responses API returns a top-level response status rather than using choices[0].finish_reason as its main completion signal.

Current response-status values include:

  • queued
  • in_progress
  • completed
  • incomplete
  • failed
  • cancelled

Inspect:

  • response.status.
  • response.error.
  • response.incomplete_details when available.
  • The status of every item in response.output.
  • response.output_text only after successful completion.

A migration that changes the endpoint from Chat Completions to Responses without changing the response parser will fail even though the model call itself is valid.

Safe Retry Decision Matrix

ConditionRetry?Required precaution
Invalid request or parameterNoCorrect the request first.
Invalid API keyNoFix authentication and Base URL.
Access deniedNoObtain permission or change the authorized model.
Model not foundNoCorrect the model, endpoint, or region.
429 request or burst rateYesBackoff, jitter, queueing, and bounded attempts.
429 exhausted quota or billingUsually no immediate retryChange quota, plan, balance, or wait for reset.
500, 502, 503, or 504YesBounded retries and request-ID logging.
Connection error before any responseOftenRetry only when the operation is idempotent.
Stream disconnected after a tool or write actionNot blindlyCheck whether the action already completed.
finish_reason="length"Not as an identical retryChange output budget or task structure.
finish_reason="tool_calls"NoExecute the tool workflow instead.

Use idempotency or an internal operation ID for payments, tickets, email, database writes, cloud operations, and other actions that must not run twice.

Node.js Production Error-Handling Example

This example disables automatic SDK retries so that the application can apply a visible bounded policy and validate finish_reason.

import OpenAI from "openai";

const apiKey = process.env.DASHSCOPE_API_KEY;
const baseURL = process.env.QWEN_BASE_URL;
const model = process.env.QWEN_MODEL ?? "qwen3.7-plus";

if (!apiKey || !baseURL) {
  throw new Error(
    "DASHSCOPE_API_KEY and QWEN_BASE_URL must be configured."
  );
}

const client = new OpenAI({
  apiKey,
  baseURL,
  timeout: 300_000,
  maxRetries: 0,
});

const RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504]);

const sleep = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

function backoffMs(attempt) {
  const exponential = 1_000 * (2 ** attempt);
  const jitter = Math.floor(Math.random() * 1_000);
  return Math.min(30_000, exponential + jitter);
}

function extractProviderCode(error) {
  return (
    error?.error?.code ??
    error?.body?.error?.code ??
    error?.body?.code ??
    error?.code ??
    null
  );
}

function validateToolCalls(toolCalls) {
  if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
    throw new Error(
      "finish_reason was tool_calls but no tool calls were returned."
    );
  }

  return toolCalls.map((toolCall) => {
    const name = toolCall?.function?.name;
    const rawArguments = toolCall?.function?.arguments;

    if (!name || typeof rawArguments !== "string") {
      throw new Error("Malformed Qwen tool call.");
    }

    let argumentsObject;

    try {
      argumentsObject = JSON.parse(rawArguments);
    } catch {
      throw new Error(
        `Tool arguments are not valid JSON for ${name}: ${rawArguments}`
      );
    }

    return {
      id: toolCall.id,
      name,
      arguments: argumentsObject,
    };
  });
}

async function callQwen(messages, { tools } = {}) {
  let lastError;

  for (let attempt = 0; attempt < 5; attempt += 1) {
    try {
      const request = {
        model,
        messages,
        stream: false,
        max_completion_tokens: 2_048,
        enable_thinking: false,
      };

      if (tools) {
        request.tools = tools;
        request.tool_choice = "auto";
      }

      const completion = await client.chat.completions.create(request);
      const choice = completion.choices?.[0];

      if (!choice) {
        throw new Error("Qwen returned no choices.");
      }

      const finishReason = choice.finish_reason;
      const message = choice.message;
      const content = message?.content ?? "";
      const toolCalls = message?.tool_calls ?? [];

      if (finishReason === "length") {
        return {
          ok: false,
          kind: "truncated",
          requestId: completion.id,
          model: completion.model,
          partialContent: content,
          usage: completion.usage ?? null,
        };
      }

      if (finishReason === "tool_calls") {
        return {
          ok: true,
          kind: "tool_calls",
          requestId: completion.id,
          model: completion.model,
          assistantMessage: message,
          toolCalls: validateToolCalls(toolCalls),
          usage: completion.usage ?? null,
        };
      }

      if (finishReason === "stop") {
        if (!content && toolCalls.length === 0) {
          throw new Error(
            "Qwen stopped normally but returned no content or tool calls."
          );
        }

        return {
          ok: true,
          kind: "message",
          requestId: completion.id,
          model: completion.model,
          content,
          reasoningContent: message?.reasoning_content ?? "",
          usage: completion.usage ?? null,
        };
      }

      throw new Error(
        `Unexpected Qwen finish_reason: ${String(finishReason)}`
      );
    } catch (error) {
      lastError = error;

      const status = error?.status ?? null;
      const providerCode = extractProviderCode(error);
      const requestId =
        error?.request_id ??
        error?.headers?.get?.("x-request-id") ??
        null;

      console.error({
        attempt: attempt + 1,
        status,
        providerCode,
        requestId,
        message: error?.message ?? String(error),
      });

      const retryable =
        RETRYABLE_STATUSES.has(status) ||
        error?.name === "APIConnectionError" ||
        error?.name === "APITimeoutError";

      if (!retryable || attempt === 4) {
        throw error;
      }

      await sleep(backoffMs(attempt));
    }
  }

  throw lastError;
}

const result = await callQwen([
  {
    role: "user",
    content: "Reply with exactly: Qwen API test passed",
  },
]);

console.log(result);

For a real tool workflow, execute only allowlisted functions, validate every argument, append the returned assistant tool-call message, and then append matching tool-result messages before the next Qwen request.

Python Streaming Validation Example

This example verifies that the stream reaches a terminal finish reason and correctly handles the optional usage-only chunk.

import os

from openai import OpenAI

api_key = os.getenv("DASHSCOPE_API_KEY")
base_url = os.getenv("QWEN_BASE_URL")
model = os.getenv("QWEN_MODEL", "qwen3.7-plus")

if not api_key or not base_url:
    raise RuntimeError(
        "DASHSCOPE_API_KEY and QWEN_BASE_URL must be configured."
    )

client = OpenAI(
    api_key=api_key,
    base_url=base_url,
    timeout=300.0,
    max_retries=0,
)

stream = client.chat.completions.create(
    model=model,
    messages=[
        {
            "role": "user",
            "content": "Explain API retries in five short sentences.",
        }
    ],
    stream=True,
    stream_options={"include_usage": True},
    extra_body={
        "enable_thinking": False,
    },
)

content_parts: list[str] = []
reasoning_parts: list[str] = []
final_finish_reason: str | None = None
usage = None

for chunk in stream:
    if getattr(chunk, "usage", None) is not None:
        usage = chunk.usage

    # The final usage-only chunk can have an empty choices array.
    if not chunk.choices:
        continue

    choice = chunk.choices[0]
    delta = choice.delta

    content = getattr(delta, "content", None)
    if content:
        content_parts.append(content)

    reasoning = getattr(delta, "reasoning_content", None)
    if reasoning:
        reasoning_parts.append(reasoning)

    if choice.finish_reason is not None:
        final_finish_reason = choice.finish_reason

if final_finish_reason is None:
    raise RuntimeError(
        "Qwen stream ended without a terminal finish_reason."
    )

result = {
    "model": model,
    "finish_reason": final_finish_reason,
    "content": "".join(content_parts),
    "reasoning_content": "".join(reasoning_parts),
    "usage": usage,
}

if final_finish_reason == "length":
    raise RuntimeError(
        f"Qwen output was truncated: {result}"
    )

if final_finish_reason == "tool_calls":
    raise RuntimeError(
        "This simple streaming example did not implement tool-call assembly."
    )

if final_finish_reason != "stop":
    raise RuntimeError(
        f"Unexpected finish_reason: {final_finish_reason}"
    )

print(result)

A production streaming tool-call client must also assemble partial function names and argument strings across multiple chunks.

Logging and Monitoring Checklist

Record enough information to diagnose the problem without storing unnecessary confidential content.

  • Timestamp and time zone.
  • Request ID.
  • HTTP status.
  • Provider error code and message.
  • Model ID.
  • API protocol and endpoint region.
  • SDK name and version.
  • Streaming or non-streaming mode.
  • Thinking-mode configuration.
  • finish_reason.
  • Input, output, reasoning, and cached token usage.
  • Latency and Time to First Token.
  • Retry number and delay.
  • Whether a tool or external action had already executed.

Do not log API keys, passwords, full private documents, active tokens, unredacted personal data, or complete confidential prompts. Use hashes or internal request references when possible.

What Not to Do

  • Do not retry unchanged 400, 401, 403, or 404 requests.
  • Do not treat every 429 as temporary request-frequency throttling.
  • Do not accept finish_reason="length" as a complete result.
  • Do not interpret intermediate null as an error.
  • Do not execute tool arguments without validation and authorization.
  • Do not send a tool-result message without the preceding assistant tool-call message.
  • Do not assume a third-party or self-hosted server reproduces official finish-reason behavior exactly.
  • Do not mix an API key with another plan’s or region’s Base URL.
  • Do not expose API keys in browser code, logs, screenshots, or support posts.
  • Do not retry an irreversible tool action unless it is idempotent or its prior result has been checked.

How to Report a Qwen API Problem

Provide a minimal reproducible request through a private support route.

  • Date, time, and time zone.
  • QwenCloud or Alibaba Cloud Model Studio.
  • Region and workspace.
  • Exact endpoint path.
  • Exact Model ID.
  • SDK, language, and version.
  • HTTP status.
  • Provider code and message.
  • Request ID.
  • Streaming and thinking settings.
  • finish_reason or response status.
  • Whether the direct minimal request works.
  • Whether the error is intermittent or reproducible.
  • A redacted request and response.

When reporting a streaming failure, include whether the first token arrived, the time of the last chunk, whether a terminal finish reason arrived, and whether your proxy or load balancer was involved.


Frequently Asked Questions

What are the official Qwen finish_reason values?

The current Qwen OpenAI-compatible Chat Completions documentation lists stop, length, and tool_calls. Streaming chunks use null while generation remains in progress.

What does finish_reason stop mean?

The model ended naturally or matched a configured stop sequence. Validate the content before accepting it, because stop does not guarantee that every requested field or business condition is complete.

What does finish_reason length mean?

The response reached an output limit. Treat it as truncated, increase the supported budget or reduce the task, and never parse incomplete JSON as a valid result.

What does finish_reason tool_calls mean?

Qwen wants the application to execute one or more tools. Validate the function name and arguments, run the tool, append the assistant and tool messages, and call the model again.

Is finish_reason null an error?

No, not during streaming. Intermediate chunks normally use null. It becomes a problem only when the connection ends without a later terminal value.

Why did my Qwen stream end without a finish reason?

The stream may have been interrupted by the client, network, proxy, timeout, parser, provider, or a non-standard third-party runtime. Treat the accumulated text as partial and preserve the logs.

Why does Qwen return 429 when my balance is positive?

A 429 can result from RPM, RPS, TPM, TPS, traffic-burst protection, plan capacity, or another quota. Inspect the provider code instead of checking balance alone.

Should I retry Qwen 500 and 503 errors?

Usually, with a bounded exponential-backoff policy and jitter. Preserve the request ID and avoid blindly repeating an external action that might already have completed.

Why does Qwen say model not found?

The Model ID may be misspelled, unavailable in the region, not activated, unsupported by the protocol, or confused with a Hugging Face repository name.

Why does my Qwen API key work with one endpoint but not another?

Keys can be tied to a region, billing plan, protocol, or workspace. Pay-as-you-go, Token Plan, and Coding Plan credentials are not automatically interchangeable.

What does DataInspectionFailed mean?

The provider’s safety layer blocked suspected non-compliant input or output. Modify the content rather than retrying the same request continuously.

What is the difference between max_tokens and max_completion_tokens?

For current supported Qwen thinking models, max_tokens limits the visible answer, while max_completion_tokens limits reasoning plus the visible answer. The latter is recommended for new reasoning-model integrations.

Why is my Qwen JSON invalid?

The model may not have been explicitly instructed to return JSON, the schema may not be supported, or the output may have been truncated. Check finish_reason before parsing and validate the result against a schema.

Why is content empty when Qwen calls a tool?

An assistant message can have empty text while containing valid tool_calls. Read the tool-call array instead of requiring content on that turn.

Does the Qwen Responses API use finish_reason?

Not as its main completion signal. Inspect the top-level status, the error and incomplete_details fields, and the statuses of objects inside the output array.

How should I log Qwen errors safely?

Log the request ID, status, provider code, model, region, latency, token usage, finish reason, and retry count. Redact API keys, credentials, personal data, private documents, and sensitive prompt content.

Official Sources and Verification

Verification status: The documented Chat Completions finish-reason values, streaming behavior, error-response formats, status codes, rate-limit categories, non-streaming timeout, thinking-mode requirements, and Responses API statuses were verified against current QwenCloud and Alibaba Cloud documentation. Retry policies, validation rules, observability fields, and production code structure are independent engineering recommendations. Self-hosted servers can differ from the official hosted APIs because their runtime, parser, and chat template are separate components.

Last verified: August 23, 2026.

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *