这是一份 Soroban Registry API 的错误码参考文档,系统整理了各类 HTTP 状态码对应的错误响应格式、常见错误原因、客户端处理建议以及 Python、JavaScript、Rust 的示例代码。文档还补充了速率限制、重试、日志追踪和可读错误提示等最佳实践。
本文档提供了 Soroban Registry API 返回的所有错误代码的全面参考,包括其含义、原因以及如何在客户端应用程序中处理这些错误。
所有 API 错误都遵循一致的 JSON 结构:
{
"error_code": "BAD_REQUEST",
"message": "Human-readable error description",
"details": {
"reason": "INVALID_CONTRACT_ID",
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
},
"timestamp": "2026-02-24T12:34:56Z"
}
支持的标准顶级 error_code 值源自 HTTP 状态类:
BAD_REQUEST
UNAUTHORIZED
FORBIDDEN
NOT_FOUND
CONFLICT
UNPROCESSABLE_ENTITY
PAYLOAD_TOO_LARGE
RATE_LIMITED
INTERNAL_ERROR
details 包含用于客户端处理的特定端点上下文,例如验证字段错误:
{
"error_code": "BAD_REQUEST",
"message": "Validation failed for 2 fields",
"details": {
"reason": "VALIDATION_ERROR",
"field_errors": [
{"field": "contract_id", "message": "Invalid format"},
{"field": "network", "message": "Unsupported network"}
],
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
| 字段 | 类型 | 描述 |
|---|---|---|
error_code |
string | 标准化的机器可读错误类 |
message |
string | 人类可读的错误描述 |
details |
object | 附加上下文(可选,因端点而异) |
timestamp |
string | 错误发生时的 ISO 8601 时间戳 |
| 代码 | 状态 | 含义 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功 |
| 202 | Accepted | 请求已接受进行异步处理 |
| 204 | No Content | 请求成功且无响应体 |
客户端错误表示请求无效。用户可以修复这些错误。
请求格式错误或包含无效参数。
常见错误代码:
{
"error": "ERR_INVALID_INPUT",
"message": "Invalid input parameters",
"code": 400,
"details": {
"field": "contract_id",
"reason": "Contract ID must be 56 characters long"
}
}
原因:
客户端操作: 在发送前验证输入。查看 API 文档以获取正确的格式。
示例 (Python):
def validate_contract_id(contract_id: str) -> bool:
return len(contract_id) == 56 and contract_id[0] == 'C'
if not validate_contract_id(contract_id):
raise ValueError("Invalid contract ID format")
{
"error": "ERR_INVALID_NETWORK",
"message": "Invalid network specified",
"code": 400,
"details": {
"network": "mainnett",
"valid_networks": ["mainnet", "testnet", "futurenet"]
}
}
原因:
客户端操作: 使用以下之一:mainnet、testnet、futurenet
{
"error": "ERR_INVALID_PAGINATION",
"message": "Invalid pagination parameters",
"code": 400,
"details": {
"limit": 10000,
"max_limit": 1000
}
}
原因:
limit 超过最大值 (1000)offset 为负数客户端操作: 使用 limit <= 1000 和有效的分页参数。
{
"error": "ERR_MALFORMED_JSON",
"message": "Request body is not valid JSON",
"code": 400,
"details": {
"parse_error": "Expected ',' at line 5, column 12"
}
}
原因:
客户端操作: 在发送前验证 JSON。使用 JSON.stringify() 或等效方法。
需要身份验证,但未提供或无效。
{
"error": "ERR_MISSING_AUTH",
"message": "Authentication required for this endpoint",
"code": 401
}
原因:
Authorization 标头客户端操作: 在 Authorization 标头中包含有效的 API key。
示例:
GET /api/contracts/private
Authorization: Bearer your-api-key-here
{
"error": "ERR_INVALID_TOKEN",
"message": "Invalid or expired authentication token",
"code": 401
}
原因:
客户端操作: 获取新的 API key 或刷新 Token。
客户端已通过身份验证,但没有访问该资源的权限。
{
"error": "ERR_FORBIDDEN",
"message": "You don't have permission to access this resource",
"code": 403,
"details": {
"required_permission": "admin:write"
}
}
原因:
客户端操作: 申请适当的权限或联系管理员。
请求的资源不存在。
{
"error": "ERR_CONTRACT_NOT_FOUND",
"message": "Contract not found",
"code": 404,
"details": {
"contract_id": "CDLZFC3...",
"network": "mainnet"
}
}
原因:
客户端操作: 验证合约 ID 和网络。检查合约是否在链上存在。
{
"error": "ERR_PUBLISHER_NOT_FOUND",
"message": "Publisher not found",
"code": 404,
"details": {
"publisher_id": "pub_abc123"
}
}
原因:
客户端操作: 验证发布者 ID 或按名称搜索。
{
"error": "ERR_VERIFICATION_NOT_FOUND",
"message": "Verification record not found",
"code": 404,
"details": {
"verification_id": "ver_xyz789"
}
}
原因:
客户端操作: 检查验证 ID 或发起新的验证。
请求与资源的当前状态冲突。
{
"error": "ERR_ALREADY_EXISTS",
"message": "Resource already exists",
"code": 409,
"details": {
"resource_type": "contract",
"contract_id": "CDLZFC3..."
}
}
原因:
客户端操作: 使用 UPDATE 端点而不是 CREATE,或检查现有资源。
{
"error": "ERR_VERIFICATION_IN_PROGRESS",
"message": "Verification already in progress for this contract",
"code": 409,
"details": {
"verification_id": "ver_abc123",
"status": "pending"
}
}
原因:
客户端操作: 等待现有验证完成或先将其取消。
请求格式正确,但语义无效。
{
"error": "ERR_INVALID_CONTRACT_SOURCE",
"message": "Contract source code is invalid",
"code": 422,
"details": {
"reason": "Missing Cargo.toml",
"required_files": ["Cargo.toml", "src/lib.rs"]
}
}
原因:
客户端操作: 确保源代码包含所有必填文件。
{
"error": "ERR_UNSUPPORTED_COMPILER",
"message": "Compiler version not supported",
"code": 422,
"details": {
"requested_version": "19.0.0",
"supported_versions": ["20.0.0", "20.5.0", "21.0.0", "21.2.0"]
}
}
原因:
客户端操作: 使用受支持的编译器版本。
超出频率限制。参见 API 频率限制。
{
"error": "ERR_RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after the indicated time.",
"code": 429,
"timestamp": "2026-02-24T12:34:56Z",
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
响应标头:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 42
Retry-After: 42
原因:
客户端操作:
Retry-After 秒X-RateLimit-Remaining 标头示例 (Python):
import time
response = requests.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
response = requests.get(url) # 重试
服务器错误表示服务器端出现问题。用户应使用指数退避进行重试。
意外的服务器错误。
{
"error": "ERR_INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred",
"code": 500,
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
原因:
客户端操作:
correlation_id 报告问题示例 (JavaScript):
async function retryOnError(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 500 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 指数退避
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
{
"error": "ERR_DATABASE_ERROR",
"message": "Database operation failed",
"code": 500,
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
原因:
客户端操作: 在短暂延迟后重试。如果问题持续存在,请报告问题。
{
"error": "ERR_COMPILATION_ERROR",
"message": "Contract compilation failed",
"code": 500,
"details": {
"compiler_output": "error: could not compile `contract`..."
}
}
原因:
客户端操作: 检查源代码的有效性。如果源码正确,请报告问题。
上游服务(例如 Stellar RPC)不可用。
{
"error": "ERR_RPC_ERROR",
"message": "Failed to communicate with Stellar RPC",
"code": 502,
"details": {
"rpc_endpoint": "https://soroban-testnet.stellar.org",
"reason": "Connection timeout"
}
}
原因:
客户端操作: 使用指数退避重试。检查 Stellar 网络状态。
服务暂时不可用。
{
"error": "ERR_SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable",
"code": 503,
"details": {
"reason": "Maintenance in progress",
"retry_after": 300
}
}
响应标头:
Retry-After: 300
原因:
客户端操作: 等待 Retry-After 秒后重试。
{
"error": "ERR_MAINTENANCE_MODE",
"message": "System is in maintenance mode",
"code": 503,
"details": {
"estimated_duration": "30 minutes",
"started_at": "2026-02-24T12:00:00Z"
}
}
原因:
客户端操作: 等待维护完成。检查状态页面。
上游服务响应时间过长。
{
"error": "ERR_TIMEOUT",
"message": "Request timeout",
"code": 504,
"details": {
"timeout_seconds": 30,
"operation": "contract_verification"
}
}
原因:
客户端操作: 重试请求。对于验证操作,考虑使用异步端点。
{
"error": "ERR_BYTECODE_MISMATCH",
"message": "Compiled bytecode does not match on-chain bytecode",
"code": 422,
"details": {
"expected_hash": "a3f2b8c9d1e4f5a6b7c8d9e0...",
"actual_hash": "9f1a2b3c4d5e6f7a8b9c0d1e...",
"possible_causes": [
"Wrong compiler version",
"Different optimization level",
"Dependency version mismatch"
]
}
}
客户端操作: 参见 验证故障排除。
{
"error": "ERR_COMPILATION_FAILED",
"message": "Source code failed to compile",
"code": 422,
"details": {
"compiler_output": "error[E0425]: cannot find function `transfer` in this scope\n --> src/lib.rs:42:5"
}
}
客户端操作: 修复源代码中的编译错误。
{
"error": "ERR_ABI_MISMATCH",
"message": "Contract ABI does not match declared interface",
"code": 422,
"details": {
"function": "transfer",
"expected_signature": "(Address, Address, i128)",
"actual_signature": "(Address, i128)"
}
}
客户端操作: 确保源代码与部署版本匹配。
response = requests.get(url)
if response.status_code >= 400:
error_data = response.json()
handle_error(error_data)
async function callApi(url) {
const response = await fetch(url);
if (!response.ok) {
const error = await response.json();
throw new ApiError(
error.error,
error.message,
error.correlation_id
);
}
return response.json();
}
重试针对:
不要重试针对:
async fn call_with_retry<T>(
operation: impl Fn() -> Result<T>,
max_retries: u32,
) -> Result<T> {
for attempt in 0..max_retries {
match operation() {
Ok(value) => return Ok(value),
Err(e) if e.is_retryable() && attempt < max_retries - 1 => {
let delay = Duration::from_secs(2_u64.pow(attempt));
tokio::time::sleep(delay).await;
continue;
}
Err(e) => return Err(e),
}
}
unreachable!()
}
始终记录 correlation_id 以便调试:
try:
response = api_client.get_contract(contract_id)
except ApiError as e:
logger.error(
f"API error: {e.error_code}",
extra={
"correlation_id": e.correlation_id,
"contract_id": contract_id
}
)
raise
class ApiClient {
async request(url) {
const response = await fetch(url);
// 检查频率限制标头
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
if (remaining < 10) {
console.warn(`Only ${remaining} requests remaining!`);
}
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After'));
await this.sleep(retryAfter * 1000);
return this.request(url); // 重试
}
return response.json();
}
}
不要直接向最终用户展示原始错误代码:
USER_FRIENDLY_MESSAGES = {
"ERR_CONTRACT_NOT_FOUND": "We couldn't find that contract. Please check the contract ID.",
"ERR_RATE_LIMIT_EXCEEDED": "Too many requests. Please wait a moment and try again.",
"ERR_INTERNAL_SERVER_ERROR": "Something went wrong on our end. Please try again later.",
}
def format_error_for_user(error_code):
return USER_FRIENDLY_MESSAGES.get(
error_code,
"An unexpected error occurred. Please try again."
)
import requests
import time
from typing import Optional
class SorobanRegistryClient:
def __init__(self, base_url: str, api_key: Optional[str] = None):
self.base_url = base_url
self.session = requests.Session()
if api_key:
self.session.headers['Authorization'] = f'Bearer {api_key}'
def get_contract(self, contract_id: str):
try:
response = self.session.get(f'{self.base_url}/api/contracts/{contract_id}')
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
if e.response.status_code == 404:
raise ContractNotFoundError(contract_id)
elif e.response.status_code == 429:
retry_after = int(e.response.headers.get('Retry-After', 60))
raise RateLimitError(retry_after)
elif e.response.status_code >= 500:
error_data = e.response.json()
raise ServerError(error_data['correlation_id'])
else:
raise
interface ApiError {
error: string;
message: string;
code: number;
correlation_id: string;
details?: any;
}
class SorobanRegistryClient {
constructor(
private baseUrl: string,
private apiKey?: string
) {}
async getContract(contractId: string) {
const response = await fetch(
`${this.baseUrl}/api/contracts/${contractId}`,
{
headers: this.apiKey
? { 'Authorization': `Bearer ${this.apiKey}` }
: {}
}
);
if (!response.ok) {
const error: ApiError = await response.json();
switch (error.code) {
case 404:
throw new ContractNotFoundError(contractId);
case 429:
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
throw new RateLimitError(retryAfter);
case 500:
case 502:
case 503:
throw new ServerError(error.correlation_id);
default:
throw new ApiError(error.error, error.message);
}
}
return response.json();
}
}
use reqwest::StatusCode;
use serde::Deserialize;
##[derive(Debug, Deserialize)]
struct ApiError {
error: String,
message: String,
code: u16,
correlation_id: String,
}
pub async fn get_contract(
client: &reqwest::Client,
base_url: &str,
contract_id: &str,
) -> Result<Contract, Box<dyn std::error::Error>> {
let url = format!("{}/api/contracts/{}", base_url, contract_id);
let response = client.get(&url).send().await?;
match response.status() {
StatusCode::OK => {
let contract = response.json::<Contract>().await?;
Ok(contract)
}
StatusCode::NOT_FOUND => {
Err(format!("Contract {} not found", contract_id).into())
}
StatusCode::TOO_MANY_REQUESTS => {
let retry_after = response
.headers()
.get("retry-after")
.and_then(|h| h.to_str().ok())
.and_then(|s| s.parse::<u64>().ok())
.unwrap_or(60);
Err(format!("Rate limited. Retry after {}s", retry_after).into())
}
_ if response.status().is_server_error() => {
let error = response.json::<ApiError>().await?;
Err(format!(
"Server error: {} (correlation_id: {})",
error.message, error.correlation_id
)
.into())
}
_ => {
let error = response.json::<ApiError>().await?;
Err(error.message.into())
}
}
}
关于错误相关的问题:
api, errors
- 原文链接: github.com/ALIPHATICHYD/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!