Solução de Problemas
Soluções para problemas comuns e dicas de solução de problemas
Problemas de Autenticação
Chave API Inválida
Erro: AUTH_INVALID - Chave API inválida
Causas Comuns:
- Chave API incorreta ou mal formatada
- Chave API expirada
- Chave API não ativada
- Espaços em branco na chave
Soluções:
// ✅ Verificar formato da chave
const apiKey = process.env.QWEN_API_KEY?.trim();
if (!apiKey || !apiKey.startsWith('qwen_')) {
throw new Error('Chave API inválida ou não encontrada');
}
// ✅ Testar chave API
async function testarChaveAPI(apiKey) {
try {
const editor = new QwenImageEdit({ apiKey });
const quota = await editor.getQuota();
console.log('✅ Chave API válida:', quota);
return true;
} catch (error) {
console.error('❌ Chave API inválida:', error.message);
return false;
}
}
// ✅ Verificar variáveis de ambiente
console.log('Chave API carregada:', process.env.QWEN_API_KEY ? 'Sim' : 'Não');
console.log('Comprimento da chave:', process.env.QWEN_API_KEY?.length || 0);
Região Não Suportada
Erro: AUTH_INVALID - Região não suportada
Soluções:
// ✅ Regiões válidas
const regioesValidas = ['us-east-1', 'us-west-1', 'eu-west-1', 'ap-southeast-1'];
const regiao = process.env.QWEN_REGION || 'us-east-1';
if (!regioesValidas.includes(regiao)) {
throw new Error(`Região inválida: ${regiao}. Use: ${regioesValidas.join(', ')}`);
}
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
region: regiao
});
Problemas de Cota e Rate Limiting
Cota Excedida
Erro: QUOTA_EXCEEDED - Cota mensal excedida
Soluções:
// ✅ Verificar cota antes de fazer requisições
async function verificarCota(editor) {
try {
const quota = await editor.getQuota();
const porcentagemUsada = (quota.requisicoes.usadas / quota.requisicoes.limite) * 100;
if (porcentagemUsada > 90) {
console.warn(`⚠️ Cota quase esgotada: ${porcentagemUsada.toFixed(1)}%`);
}
if (quota.requisicoes.usadas >= quota.requisicoes.limite) {
throw new Error('Cota excedida. Aguarde o reset ou faça upgrade do plano.');
}
return quota;
} catch (error) {
console.error('Erro ao verificar cota:', error.message);
throw error;
}
}
// ✅ Implementar retry com verificação de cota
async function editTextComVerificacao(editor, opcoes) {
await verificarCota(editor);
return editor.editText(opcoes);
}
Rate Limit Atingido
Erro: RATE_LIMITED - Muitas requisições
Soluções:
// ✅ Implementar backoff exponencial
class EditorComRateLimit {
constructor(editor) {
this.editor = editor;
this.requestQueue = [];
this.isProcessing = false;
this.rateLimitDelay = 1000; // 1 segundo inicial
}
async editText(opcoes) {
return new Promise((resolve, reject) => {
this.requestQueue.push({ opcoes, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.isProcessing || this.requestQueue.length === 0) {
return;
}
this.isProcessing = true;
while (this.requestQueue.length > 0) {
const { opcoes, resolve, reject } = this.requestQueue.shift();
try {
const resultado = await this.editor.editText(opcoes);
resolve(resultado);
// Reset delay em caso de sucesso
this.rateLimitDelay = 1000;
} catch (error) {
if (error.codigo === 'RATE_LIMITED') {
// Aumentar delay e recolocar na fila
this.rateLimitDelay = Math.min(this.rateLimitDelay * 2, 30000);
this.requestQueue.unshift({ opcoes, resolve, reject });
console.log(`Rate limit atingido. Aguardando ${this.rateLimitDelay}ms...`);
await new Promise(r => setTimeout(r, this.rateLimitDelay));
} else {
reject(error);
}
}
}
this.isProcessing = false;
}
}
const editorComRateLimit = new EditorComRateLimit(editor);
Problemas com Imagens
Imagem Muito Grande
Erro: IMAGE_TOO_LARGE - Imagem excede o tamanho máximo
Soluções:
import sharp from 'sharp';
import fs from 'fs';
// ✅ Verificar tamanho antes de enviar
async function verificarTamanhoImagem(caminhoImagem) {
const stats = fs.statSync(caminhoImagem);
const tamanhoMB = stats.size / (1024 * 1024);
console.log(`Tamanho da imagem: ${tamanhoMB.toFixed(2)}MB`);
if (tamanhoMB > 10) { // Limite de 10MB
throw new Error(`Imagem muito grande: ${tamanhoMB.toFixed(2)}MB. Máximo: 10MB`);
}
return stats;
}
// ✅ Redimensionar automaticamente
async function redimensionarSeNecessario(caminhoImagem, tamanhoMaxMB = 5) {
const stats = fs.statSync(caminhoImagem);
const tamanhoAtualMB = stats.size / (1024 * 1024);
if (tamanhoAtualMB <= tamanhoMaxMB) {
return caminhoImagem;
}
console.log(`Redimensionando imagem de ${tamanhoAtualMB.toFixed(2)}MB...`);
const metadata = await sharp(caminhoImagem).metadata();
// Calcular nova dimensão mantendo proporção
const fatorReducao = Math.sqrt(tamanhoMaxMB / tamanhoAtualMB);
const novaLargura = Math.round(metadata.width * fatorReducao);
const novaAltura = Math.round(metadata.height * fatorReducao);
const imagemRedimensionada = await sharp(caminhoImagem)
.resize(novaLargura, novaAltura)
.jpeg({ quality: 85 })
.toBuffer();
console.log(`Imagem redimensionada para ${(imagemRedimensionada.length / (1024 * 1024)).toFixed(2)}MB`);
return imagemRedimensionada;
}
// ✅ Usar com editor
async function editTextSeguro(editor, opcoes) {
if (typeof opcoes.imagem === 'string') {
opcoes.imagem = await redimensionarSeNecessario(opcoes.imagem);
}
return editor.editText(opcoes);
}
Formato de Imagem Não Suportado
Erro: IMAGE_FORMAT_UNSUPPORTED - Formato de imagem não suportado
Soluções:
import sharp from 'sharp';
import path from 'path';
// ✅ Verificar formato suportado
const formatosSuportados = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff'];
function verificarFormato(caminhoImagem) {
const extensao = path.extname(caminhoImagem).toLowerCase().slice(1);
if (!formatosSuportados.includes(extensao)) {
throw new Error(`Formato não suportado: ${extensao}. Use: ${formatosSuportados.join(', ')}`);
}
return extensao;
}
// ✅ Converter para formato suportado
async function converterParaJPEG(caminhoImagem) {
try {
const formato = verificarFormato(caminhoImagem);
if (['jpg', 'jpeg'].includes(formato)) {
return caminhoImagem;
}
console.log(`Convertendo ${formato} para JPEG...`);
const imagemConvertida = await sharp(caminhoImagem)
.jpeg({ quality: 90 })
.toBuffer();
return imagemConvertida;
} catch (error) {
console.error('Erro na conversão:', error.message);
throw new Error('Não foi possível converter a imagem');
}
}
// ✅ Detectar formato automaticamente
async function detectarEConverterFormato(imagem) {
try {
const metadata = await sharp(imagem).metadata();
if (!['jpeg', 'png', 'webp'].includes(metadata.format)) {
console.log(`Convertendo ${metadata.format} para JPEG...`);
return await sharp(imagem).jpeg({ quality: 90 }).toBuffer();
}
return imagem;
} catch (error) {
throw new Error('Formato de imagem inválido ou corrompido');
}
}
Problemas de Rede
Timeout de Conexão
Erro: NETWORK_ERROR - Connection timeout
Soluções:
// ✅ Configurar timeouts apropriados
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
timeout: 60000, // 60 segundos
uploadTimeout: 120000, // 2 minutos para upload
downloadTimeout: 60000 // 1 minuto para download
});
// ✅ Implementar retry com timeout progressivo
class EditorComTimeoutProgressivo {
constructor(editor) {
this.editor = editor;
this.timeouts = [30000, 60000, 120000]; // 30s, 60s, 120s
}
async editTextComRetry(opcoes, tentativa = 0) {
const timeout = this.timeouts[tentativa] || this.timeouts[this.timeouts.length - 1];
try {
// Criar nova instância com timeout específico
const editorComTimeout = new QwenImageEdit({
...this.editor.config,
timeout: timeout
});
console.log(`Tentativa ${tentativa + 1} com timeout de ${timeout}ms`);
return await editorComTimeout.editText(opcoes);
} catch (error) {
if (error.codigo === 'TIMEOUT' && tentativa < this.timeouts.length - 1) {
console.log(`Timeout na tentativa ${tentativa + 1}, tentando novamente...`);
return this.editTextComRetry(opcoes, tentativa + 1);
}
throw error;
}
}
}
Erro de Rede
Erro: NETWORK_ERROR - Network error
Soluções:
// ✅ Verificar conectividade
async function verificarConectividade() {
try {
const response = await fetch('https://api.qwen.com/health', {
method: 'GET',
timeout: 5000
});
if (response.ok) {
console.log('✅ Conectividade OK');
return true;
} else {
console.error('❌ API indisponível:', response.status);
return false;
}
} catch (error) {
console.error('❌ Erro de rede:', error.message);
return false;
}
}
// ✅ Implementar retry com verificação de rede
async function editTextComVerificacaoRede(editor, opcoes) {
const maxTentativas = 3;
for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
try {
// Verificar conectividade antes de tentar
if (!(await verificarConectividade())) {
throw new Error('Sem conectividade com a API');
}
return await editor.editText(opcoes);
} catch (error) {
if (error.codigo === 'NETWORK_ERROR' && tentativa < maxTentativas) {
const delay = tentativa * 2000; // 2s, 4s, 6s
console.log(`Erro de rede na tentativa ${tentativa}. Aguardando ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
}
Problemas de Processamento
Falha no Processamento
Erro: PROCESSING_FAILED - Falha no processamento da imagem
Soluções:
// ✅ Validar imagem antes do processamento
async function validarImagem(imagem) {
try {
let buffer;
if (typeof imagem === 'string') {
if (imagem.startsWith('data:')) {
// Base64
buffer = Buffer.from(imagem.split(',')[1], 'base64');
} else if (imagem.startsWith('http')) {
// URL
const response = await fetch(imagem);
buffer = Buffer.from(await response.arrayBuffer());
} else {
// Arquivo local
buffer = fs.readFileSync(imagem);
}
} else {
buffer = imagem;
}
// Verificar se é uma imagem válida
const metadata = await sharp(buffer).metadata();
if (!metadata.width || !metadata.height) {
throw new Error('Imagem corrompida ou inválida');
}
console.log(`✅ Imagem válida: ${metadata.width}x${metadata.height}, ${metadata.format}`);
return {
buffer,
metadata,
tamanho: buffer.length
};
} catch (error) {
throw new Error(`Imagem inválida: ${error.message}`);
}
}
// ✅ Pré-processar imagem para melhor compatibilidade
async function preprocessarImagem(imagem) {
const { buffer, metadata } = await validarImagem(imagem);
// Normalizar imagem
const imagemNormalizada = await sharp(buffer)
.removeAlpha() // Remover canal alpha se presente
.jpeg({ quality: 95 }) // Converter para JPEG de alta qualidade
.toBuffer();
return imagemNormalizada;
}
Conteúdo Não Detectado
Erro: CONTENT_NOT_DETECTED - Conteúdo especificado não encontrado
Soluções:
// ✅ Analisar imagem antes de editar
async function analisarAntesDeEditar(editor, imagem, prompt) {
try {
// Primeiro analisar o que há na imagem
const analise = await editor.analyzeImage({
imagem,
detectar: ['texto', 'objetos']
});
console.log('Texto detectado:', analise.texto.map(t => t.conteudo));
console.log('Objetos detectados:', analise.objetos.map(o => o.nome));
// Verificar se o prompt é compatível com o conteúdo
if (prompt.includes('texto') && analise.texto.length === 0) {
throw new Error('Nenhum texto detectado na imagem para edição');
}
return analise;
} catch (error) {
console.error('Erro na análise:', error.message);
throw error;
}
}
// ✅ Prompt mais específico baseado na análise
async function editTextInteligente(editor, imagem, promptBase) {
const analise = await analisarAntesDeEditar(editor, imagem, promptBase);
// Ajustar prompt baseado no conteúdo detectado
let promptAjustado = promptBase;
if (analise.texto.length > 0) {
const textoExistente = analise.texto[0].conteudo;
promptAjustado = `${promptBase}. Texto atual detectado: "${textoExistente}"`;
}
return editor.editText({
imagem,
prompt: promptAjustado
});
}
Problemas de Performance
Processamento Lento
Soluções:
// ✅ Otimizar imagens antes do processamento
async function otimizarParaVelocidade(imagem) {
const metadata = await sharp(imagem).metadata();
// Reduzir resolução se muito alta
let largura = metadata.width;
let altura = metadata.height;
if (largura > 2048 || altura > 2048) {
const fator = Math.min(2048 / largura, 2048 / altura);
largura = Math.round(largura * fator);
altura = Math.round(altura * fator);
console.log(`Reduzindo resolução para ${largura}x${altura} para melhor performance`);
}
return sharp(imagem)
.resize(largura, altura)
.jpeg({ quality: 80 })
.toBuffer();
}
// ✅ Processamento em paralelo com limite
import pLimit from 'p-limit';
const limit = pLimit(3); // Máximo 3 requisições simultâneas
async function processarImagensEmLote(editor, imagens) {
const resultados = await Promise.all(
imagens.map(imagem =>
limit(async () => {
const imagemOtimizada = await otimizarParaVelocidade(imagem.buffer);
return editor.editText({
imagem: imagemOtimizada,
prompt: imagem.prompt
});
})
)
);
return resultados;
}
Insuficiência de Memória
Soluções:
// ✅ Gestão de memória para imagens grandes
class GestorMemoria {
constructor(limiteMemoriaMB = 500) {
this.limiteMemoria = limiteMemoriaMB * 1024 * 1024;
this.usoAtual = 0;
this.cache = new Map();
}
async processarImagem(editor, imagem, prompt) {
const tamanhoImagem = Buffer.isBuffer(imagem) ? imagem.length :
fs.statSync(imagem).size;
// Verificar se há memória suficiente
if (this.usoAtual + tamanhoImagem > this.limiteMemoria) {
await this.liberarMemoria();
}
this.usoAtual += tamanhoImagem;
try {
const resultado = await editor.editText({ imagem, prompt });
return resultado;
} finally {
this.usoAtual -= tamanhoImagem;
// Forçar garbage collection se disponível
if (global.gc) {
global.gc();
}
}
}
async liberarMemoria() {
// Limpar cache
this.cache.clear();
// Aguardar um pouco para GC
await new Promise(resolve => setTimeout(resolve, 100));
if (global.gc) {
global.gc();
}
this.usoAtual = 0;
}
}
const gestorMemoria = new GestorMemoria(500); // 500MB limite
Ferramentas de Debug
Logging Detalhado
// ✅ Logger personalizado para debug
class QwenDebugger {
constructor(editor) {
this.editor = editor;
this.logs = [];
}
async editTextComLog(opcoes) {
const requestId = this.gerarRequestId();
const startTime = Date.now();
this.log(requestId, 'INICIO', 'Iniciando edição de texto', {
prompt: opcoes.prompt,
temImagem: !!opcoes.imagem
});
try {
// Validar entrada
await this.validarEntrada(requestId, opcoes);
// Fazer requisição
const resultado = await this.editor.editText(opcoes);
const duracao = Date.now() - startTime;
this.log(requestId, 'SUCESSO', 'Edição concluída', {
duracao: `${duracao}ms`,
creditos: resultado.creditos
});
return resultado;
} catch (error) {
const duracao = Date.now() - startTime;
this.log(requestId, 'ERRO', error.message, {
duracao: `${duracao}ms`,
codigo: error.codigo,
stack: error.stack
});
throw error;
}
}
async validarEntrada(requestId, opcoes) {
// Validar imagem
if (typeof opcoes.imagem === 'string' && opcoes.imagem.startsWith('./')) {
const existe = fs.existsSync(opcoes.imagem);
this.log(requestId, 'VALIDACAO', `Arquivo existe: ${existe}`, {
caminho: opcoes.imagem
});
if (!existe) {
throw new Error(`Arquivo não encontrado: ${opcoes.imagem}`);
}
}
// Validar prompt
if (!opcoes.prompt || opcoes.prompt.trim().length === 0) {
throw new Error('Prompt não pode estar vazio');
}
this.log(requestId, 'VALIDACAO', 'Entrada válida');
}
log(requestId, nivel, mensagem, dados = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
requestId,
nivel,
mensagem,
dados
};
this.logs.push(logEntry);
console.log(`[${nivel}] ${requestId}: ${mensagem}`, dados);
}
gerarRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
obterLogs(requestId = null) {
return requestId ?
this.logs.filter(log => log.requestId === requestId) :
this.logs;
}
}
const debugger = new QwenDebugger(editor);
Interceptador de Requisições
// ✅ Interceptar e logar todas as requisições
class RequestInterceptor {
constructor(editor) {
this.editor = editor;
this.setupInterceptors();
}
setupInterceptors() {
// Interceptar requisições HTTP
const originalFetch = global.fetch;
global.fetch = async (url, options = {}) => {
const startTime = Date.now();
console.log('📤 Requisição HTTP:', {
url,
method: options.method || 'GET',
headers: this.sanitizeHeaders(options.headers)
});
try {
const response = await originalFetch(url, options);
const duration = Date.now() - startTime;
console.log('📥 Resposta HTTP:', {
status: response.status,
statusText: response.statusText,
duration: `${duration}ms`
});
return response;
} catch (error) {
const duration = Date.now() - startTime;
console.error('❌ Erro HTTP:', {
error: error.message,
duration: `${duration}ms`
});
throw error;
}
};
}
sanitizeHeaders(headers) {
if (!headers) return {};
const sanitized = { ...headers };
// Ocultar informações sensíveis
if (sanitized.Authorization) {
sanitized.Authorization = 'Bearer ***';
}
return sanitized;
}
}
const interceptor = new RequestInterceptor(editor);
Health Check
// ✅ Verificação de saúde completa
async function healthCheck(editor) {
const resultados = {
timestamp: new Date().toISOString(),
status: 'unknown',
verificacoes: {}
};
try {
// 1. Verificar autenticação
console.log('🔐 Verificando autenticação...');
const quota = await editor.getQuota();
resultados.verificacoes.autenticacao = {
status: 'ok',
quota: quota
};
// 2. Verificar conectividade
console.log('🌐 Verificando conectividade...');
const startTime = Date.now();
const testImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
await editor.analyzeImage({
imagem: testImage,
detectar: ['cores']
});
const responseTime = Date.now() - startTime;
resultados.verificacoes.conectividade = {
status: 'ok',
responseTime: `${responseTime}ms`
};
// 3. Verificar funcionalidade básica
console.log('⚙️ Verificando funcionalidade...');
const resultado = await editor.editText({
imagem: testImage,
prompt: 'teste'
});
resultados.verificacoes.funcionalidade = {
status: 'ok',
creditos: resultado.creditos
};
resultados.status = 'healthy';
console.log('✅ Todas as verificações passaram');
} catch (error) {
resultados.status = 'unhealthy';
resultados.erro = {
message: error.message,
codigo: error.codigo
};
console.error('❌ Health check falhou:', error.message);
}
return resultados;
}
// Executar health check
healthCheck(editor).then(resultado => {
console.log('Health Check:', JSON.stringify(resultado, null, 2));
});
Contato para Suporte
Se você não conseguir resolver o problema com este guia:
📧 Suporte por Email
- Email: support@qwen.com
- Tempo de resposta: 24-48 horas
- Inclua: Logs de erro, código reproduzível, versão da biblioteca
💬 Comunidade
- Discord: discord.gg/qwen
- GitHub Issues: github.com/qwen/image-edit/issues
- Stack Overflow: Tag
qwen-image-edit
📊 Status do Serviço
- Status Page: status.qwen.com
- Atualizações: @QwenStatus
📋 Informações para Incluir no Suporte
// ✅ Script para coletar informações de debug
function coletarInfoDebug() {
return {
timestamp: new Date().toISOString(),
ambiente: {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch
},
biblioteca: {
versao: '1.0.0', // Versão do qwen-image-edit
configuracao: {
region: process.env.QWEN_REGION,
temChaveAPI: !!process.env.QWEN_API_KEY
}
},
erro: {
// Incluir detalhes do erro aqui
}
};
}
console.log('Info Debug:', JSON.stringify(coletarInfoDebug(), null, 2));
Não encontrou a solução? Consulte nossa documentação completa ou entre em contato com o suporte.