🩺 GUIA DE TROUBLESHOOTING E FAQ DE ENGENHARIA

Durante o desenvolvimento de software e persistência de dados, encontrar erros e exceções no terminal faz parte natural da jornada de aprendizado.

Este guia reúne os erros mais comuns enfrentados pelos estudantes, explicando a causa raiz, o que a mensagem técnica significa e o passo a passo exato para resolver o problema em segundos. 🛡️⚡


🧭 Índice Rápido de Diagnóstico

flowchart TD
    E["🚨 Encontrou um Erro?"] --> API["🌐 1. Erros de API, Flask & Pydantic (400, 404, 405, Porta 5000)"]
    E --> ORM["🛢️ 2. Erros de Banco & SQLAlchemy (IntegrityError, Lock)"]
    E --> ENV["🐳 3. Erros de Docker, Portas & Venv (Porta 5432 Ocupada)"]
    E --> MIG["🔄 4. Erros de Alembic & Migrations (Multiple Heads)"]
    E --> JINJA["🎨 5. Erros de Template Jinja2 (TemplateNotFound)"]

    style E fill:#fee2e2,stroke:#ef4444
    style API fill:#e0f2fe,stroke:#0284c7
    style ORM fill:#fef3c7,stroke:#d97706
    style ENV fill:#dcfce7,stroke:#16a34a
    style MIG fill:#f3e8ff,stroke:#9333ea
    style JINJA fill:#fce7f3,stroke:#db2777

🌐 1. Erros de API, Flask 3.x e Pydantic

❌ Erro 1.1: Porta 5000 já em uso (OSError: [WinError 10048])

  • Sintoma: Ao executar python -m app.main, o terminal exibe OSError: [WinError 10048] Only one usage of each socket address is normally permitted.
  • Causa Raiz: Uma instância anterior do servidor Flask ainda está rodando em segundo plano no Windows e segurando a porta 5000.
  • Como Resolver: Abra o PowerShell e encerre os processos Python ativos:
Get-Process python* | Stop-Process -Force

❌ Erro 1.2: 400 Bad Request ao validar payload com Pydantic

  • Sintoma: Ao enviar uma requisição POST ou PUT, a API retorna 400 Bad Request com o detalhe do erro do Pydantic ou request.get_json() retorna None.
  • Causa Raiz: O cliente não enviou o cabeçalho Content-Type: application/json ou faltou algum campo obrigatório exigido pelo Schema.
  • Como Resolver:
    1. No PowerShell com Invoke-RestMethod, garanta o parâmetro -ContentType "application/json".
    2. Verifique se o JSON contém todas as chaves exigidas pelo schema Pydantic.
    3. No handler do Flask, utilize sempre tratamento com bloco try/except:
dados = request.get_json() or {}
try:
    payload = MeuSchema(**dados)
except Exception as err:
    return jsonify({"detail": str(err)}), 400

❌ Erro 1.3: 405 Method Not Allowed

  • Sintoma: Ao submeter um formulário ou chamar um endpoint, o navegador exibe 405 Method Not Allowed.
  • Causa Raiz: O endpoint foi registrado para um verbo HTTP (ex: @app.get("/usuarios")), mas a requisição foi feita usando outro método (ex: POST).
  • Como Resolver:
    1. No formulário HTML, confira a tag: <form action="/usuarios" method="POST">.
    2. No arquivo do router Flask, certifique-se de que o método HTTP coincide:
@router.route("/usuarios", methods=["POST"])
# ou:
@router.post("/usuarios")

❌ Erro 1.4: Erro de CORS (Blocked by CORS Policy)

  • Sintoma: O frontend não consegue se comunicar com a API e o console do navegador exibe: Access to XMLHttpRequest at 'http://localhost:5000/api' has been blocked by CORS policy.
  • Causa Raiz: Por segurança, navegadores bloqueiam requisições JavaScript vindas de origens/portas diferentes.
  • Como Resolver: Adicione os headers CORS ou utilize flask-cors:
@app.after_request
def add_cors_headers(response):
    response.headers["Access-Control-Allow-Origin"] = "*"
    response.headers["Access-Control-Allow-Headers"] = "Content-Type,Authorization"
    response.headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS"
    return response

🛢️ 2. Erros de Banco de Dados & SQLAlchemy 2.0

❌ Erro 2.1: sqlalchemy.exc.IntegrityError: NOT NULL constraint failed

  • Sintoma: Falha ao tentar salvar um registro com session.commit().
  • Causa Raiz: O modelo possui uma coluna com nullable=False (ou Mapped[str] sem | None), mas o objeto foi instanciado sem preencher esse valor.
  • Como Resolver: Garanta que todos os campos obrigatórios sejam passados no construtor do modelo antes de executar session.add():
# ❌ Incorreto:
novo_cliente = Cliente(email="carlos@email.com") # Faltou o nome!

# ✅ Correto:
novo_cliente = Cliente(nome="Carlos Silva", email="carlos@email.com")

❌ Erro 2.2: sqlite3.OperationalError: database is locked

  • Sintoma: O SQLite trava e emite erro informando que o arquivo de banco de dados está bloqueado.
  • Causa Raiz: Uma sessão anterior abriu uma transação de escrita e não foi encerrada com session.commit(), session.rollback() ou session.close(), ou o banco está aberto para edição concorrente em outra ferramenta (como DB Browser).
  • Como Resolver:
    1. Garanta que a função get_db() utilize yield e finally: db.close().
    2. Feche conexões manuais abertas em ferramentas externas.
    3. No database.py, configure timeout para o SQLite:
engine = create_engine(
    "sqlite:///dev.db",
    connect_args={"timeout": 30} # Aguarda até 30s antes de falhar
)

❌ Erro 2.3: sqlalchemy.orm.exc.DetachedInstanceError

  • Sintoma: Erro ao acessar um relacionamento (ex: usuario.pedidos) no template Jinja2 ou após sair da função de rota.
  • Causa Raiz: O SQLAlchemy usa carregamento preguiçoso (Lazy Loading) por padrão. Quando a sessão do banco foi fechada pelo get_db(), o objeto perdeu a conexão ativa necessária para buscar os dados filhos.
  • Como Resolver: Utilize joinedload() ou selectinload() na consulta para trazer os relacionamentos de forma ansiosa (Eager Loading):
from sqlalchemy.orm import selectinload

stmt = select(Usuario).options(selectinload(Usuario.pedidos)).where(Usuario.id == 1)
usuario = session.scalars(stmt).first()

🐳 3. Erros de Docker, Portas e Ambiente Virtual

❌ Erro 3.1: Bind for 0.0.0.0:5432 failed: port is already allocated

  • Sintoma: Ao rodar docker compose up -d, o terminal acusa que a porta 5432 já está em uso.
  • Causa Raiz: O serviço do PostgreSQL nativo do Windows já está rodando em segundo plano e ocupando a porta 5432 da máquina física.
  • Como Resolver (Opção A — Parar o serviço do Windows):
    • Abra o PowerShell como Administrador e execute:
Stop-Service postgresql*
  • Como Resolver (Opção B — Mudar a porta no docker-compose.yml):
    • Mude o mapeamento para usar a porta 5433 no seu computador:
ports:
  - "5433:5432" # 5433 no Windows -> 5432 dentro do container
  • Atualize seu .env: DATABASE_URL=postgresql://postgres:senha@localhost:5433/meu_db.

❌ Erro 3.2: Erro de Permissão ao Ativar o Venv no PowerShell (Script Execution Policy)

  • Sintoma: Ao rodar .\venv\Scripts\Activate.ps1, o PowerShell exibe: não pode ser carregado porque a execução de scripts foi desabilitada neste sistema.
  • Causa Raiz: Política de segurança padrão do Windows para scripts locais.
  • Como Resolver: Abra o PowerShell e libere para o seu usuário atual:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

🔄 4. Erros de Migrações com Alembic

❌ Erro 4.1: alembic.util.exc.CommandError: Target database is not up to date

  • Sintoma: Erro ao tentar rodar alembic revision --autogenerate.
  • Causa Raiz: Existem migrações anteriores no histórico que ainda não foram aplicadas no banco de dados atual.
  • Como Resolver: Aplique todas as migrações pendentes antes de gerar uma nova:
alembic upgrade head
alembic revision --autogenerate -m "nova_alteracao"
alembic upgrade head

❌ Erro 4.2: Conflito de Versões (Multiple head revisions are present)

  • Sintoma: Conflito após fazer git pull de alterações de outro colega de grupo no Projeto Integrador.
  • Causa Raiz: Dois alunos criaram migrações simultaneamente em branches separadas.
  • Como Resolver: Crie uma migração de merge para unir as duas pontas:
alembic merge heads -m "merge_conflitos"
alembic upgrade head

🎨 5. Erros de Template e Renderização Jinja2

❌ Erro 5.1: jinja2.exceptions.TemplateNotFound: index.html

  • Sintoma: A aplicação Flask falha com erro de template não encontrado ao tentar renderizar a view.
  • Causa Raiz: O caminho informado no template_folder do Flask está incorreto ou a pasta templates não está no mesmo nível do diretório app/.
  • Como Resolver: Configure o caminho absoluto baseado na raiz do diretório app/:
from pathlib import Path
from flask import Flask, render_template

BASE_DIR = Path(__file__).resolve().parent
app = Flask(__name__, template_folder=str(BASE_DIR / "templates"))

❌ Erro 5.2: jinja2.exceptions.UndefinedError: 'item' is undefined

  • Sintoma: A página quebra durante a renderização no navegador.
  • Causa Raiz: O template .html tenta acessar uma variável {{ item.nome }}, mas ela não foi passada nos argumentos do render_template(...).
  • Como Resolver: Verifique a rota no main.py e certifique-se de passar o objeto nomeado no retorno:
@app.route("/detalhes/<int:id>")
def detalhes(id: int):
    item_do_banco = ...
    return render_template("detalhes.html", item=item_do_banco)

💡 Regra de Ouro do Engenheiro de Software

Não tenha medo dos erros: Leia atentamente a última linha do rastreamento (Traceback). Mais de 90% dos problemas de desenvolvimento são resolvidos conferindo tipos de dados, caminhos de arquivo e conexões ativas! 🚀🛡️