用户指南 API 参考

错误

了解 API 错误响应、WebSocket 关闭代码及其处理方式

REST API 错误格式 (HTTP)

当标准 HTTP 端点发生错误时,API 会返回具有以下结构的 JSON 响应:

{
    "success": false,
    "error": {
        "code": "ERROR_CODE",
        "message": "人类可读的错误描述",
        "details": {
          // 附加背景信息(视错误类型而定)
        }
    }
}

WebSocket 错误格式 (WSS)

WebSockets 处理错误的方式有所不同。错误可能发生在初始连接握手阶段,也可能发生在音频生成过程中的数据流传输期间。

  • 致命错误: 连接因特定的关闭代码(例如 4290)而断开。
  • 处理中途错误: 服务器在关闭前发送一个扁平的 JSON 错误帧: {"error": "直播期间积分不足"}

状态码与关闭码

HTTP 状态码 (REST)

状态 含义 描述
200 好的 请求成功
400 无效请求 请求参数无效或请求格式错误
401 未经授权 API 密钥缺失或无效
402 需付款 积分不足以完成该请求
403 禁止访问 API 密钥没有执行此操作的权限
404 未找到 请求的资源不存在
409 冲突 请求与当前状态冲突(例如,并发 HTTP 请求)
429 请求过多 超出速率限制
500 内部服务器错误 我们这边出了点问题

WebSocket 关闭代码 (WSS)

代码 类型 描述
1000 正常关闭 数据流已成功完成。
3011 生成错误 上游 API 音频生成失败。请重试请求。
4001 缺少认证信息 握手期间,Authorization 头部未提供 API 密钥。
4002 认证失败 密钥无效、账户未激活、初始额度不足或受 IP 限制。
4290 并发限制 您已有一个处于活动状态的 TTS WebSocket 连接。每个用户仅限建立 1 个连接。

常见错误场景

1. 未经授权 / 身份验证失败

HTTP: 401 WSS:4001 / 4002

由于 API 密钥缺失或无效,或者受到 IP 白名单限制,身份验证失败。

常见原因:

  • 缺少 Authorization: Bearer 请求头
  • API 密钥格式无效或密钥已被撤销
  • 从不在您密钥白名单中的 IP 地址进行连接

2. 积分不足

HTTP: 402 WSS:流中 JSON

您的账户没有足够的点数来完成此请求。

  • HTTP: 在生成音频前立即失败。
  • WebSocket: 如果在数据流传输过程中信用额度耗尽,服务器会发送 {"error": "直播期间积分不足"} 并正常关闭连接。在额度耗尽前生成的音频仍会进行传输。

3. 并发与速率限制

HTTP: 409 / 429 WSS:4290

为确保公平使用,Moknah 每次处理每个用户的单个请求。

  • HTTP 409: 您的账户已有另一项请求正在处理中,请等待其完成。
  • HTTP 429: 您超过了每分钟 60 次请求 (RPM) 的限制。
  • WSS 4290: 您尝试在第一个 WebSocket 连接仍处于活动状态时打开第二个连接。

4. 无效请求 / 错误数据

HTTP: 400 WSS:流中 JSON

请求参数无效,或者您超出了文本分块限制。

  • HTTP: 缺少必填字段(text、voice_id)。
  • WebSocket: 发送超过 2,000 个字符的文本片段将导致 {"error": "数据块过大。每个数据块最多包含 2000 个字符。"}.

处理代码中的错误

import requests
import time

def generate_speech(text, voice_id, api_key):
    url = "https://moknah.io/api/v1/tts/generate"
    headers = {"Authorization": f"Bearer {api_key}"}

    response = requests.post(url, json={"text": text, "voice_id": voice_id}, headers=headers)

    if response.status_code == 200:
        return response.content  # 音频片段
    elif response.status_code == 402:
        raise Exception("积分不足")
    elif response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 5))
        time.sleep(retry_after)
        return generate_speech(text, voice_id, api_key)  # 重试
    elif response.status_code == 409:
        time.sleep(5)
        return generate_speech(text, voice_id, api_key)  # 重试
    else:
        raise Exception(f"API error: {response.status_code}")
async function generateSpeech(text, voiceId, apiKey) {
  const response = await fetch("https://moknah.io/api/v1/tts/generate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ text: text, voice_id: voiceId })
  });

  if (response.ok) return await response.blob();

  if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After") || 5;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    return generateSpeech(text, voiceId, apiKey);
  }

  if (response.status === 409) {
    await new Promise(r => setTimeout(r, 5000));
    return generateSpeech(text, voiceId, apiKey);
  }

  const error = await response.json();
  throw new Error(error.error?.message || "API Error");
}
const ws = new WebSocket('wss://moknah.io/api/v1/api/v1/tts/ws/', {
    headers: { "Authorization": `Bearer ${apiKey}` }
});

// Handle Mid-Stream JSON Errors
ws.on('message', (data, isBinary) => {
    if (!isBinary) {
        const msg = JSON.parse(data.toString());
        if (msg.error) {
            console.error("Stream Error:", msg.error);
            // e.g., "Insufficient credits during stream"
        }
    }
});

// Handle Fatal Close Codes
ws.on('close', (code, reason) => {
    switch(code) {
        case 1000:
            console.log("Stream finished successfully.");
            break;
        case 4001:
        case 4002:
            console.error("Authentication failed. Check your API key.");
            break;
        case 4290:
            console.error("Concurrency limit reached. Close other active streams.");
            // Wait a few seconds before attempting to reconnect
            setTimeout(reconnect, 5000);
            break;
        case 3011:
            console.error("Upstream generation failed. Please try again.");
            break;
        default:
            console.error(`Disconnected with code ${code}`);
    }
});
最佳实践:指数退避

针对 429 (HTTP)、409 (HTTP) 和 4290 (WSS) 错误,请采用指数退避策略:依次等待 1 秒、2 秒、4 秒等。这既能避免 API 过载,又能确保在锁定解除后请求能够成功。

API 支持

如有关于 API 的问题或疑问,请联系我们: api@moknah.io.