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

📊 Status do Serviço

📋 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.