# BUG #3: Fixing Plan — Missing Error Handling (`_repair_supabase_if_missing`)

**Bug ID:** BUG-003  
**Severity:** 🔴 HIGH — Production Stability  
**Date:** 2026-05-12  
**Status:** PLANNING  

---

## 1. Root Cause

Fungsi `_repair_supabase_if_missing` di `ingestion.py` memiliki **3 masalah error handling**:

### Masalah A: Bare `except Exception` tanpa logging proper
```python
# SEKARANG — line ~47
try:
    from app.services.storage.supabase_client import check_supabase_hash, start_uploader_worker
except Exception:                                          # ← terlalu broad, tidak ada info error
    print("Supabase client utilities not available...")   # ← print(), bukan logger
    return
```

### Masalah B: Silent failure pada `check_supabase_hash`
```python
# SEKARANG — line ~52
try:
    exists_in_supabase = check_supabase_hash(file_hash)
except Exception as e:
    print(f"Error checking Supabase for hash {file_hash}: {e}")  # ← print(), bukan logger
    return  # ← diam-diam berhenti, tidak ada alerting
```

### Masalah C: Silent failure pada blok repair utama
```python
# SEKARANG — line ~88
except Exception as e:
    print(f"Exception during Supabase repair for {file_hash}: {e}")  # ← print(), bukan logger
    return  # ← tidak ada stack trace, tidak ada retry
```

**Dampak di production:**
- Error hanya muncul di stdout, tidak masuk ke log file
- Tidak ada stack trace → sulit debug
- Tidak ada perbedaan severity (warning vs error vs critical)
- Repair gagal secara diam-diam tanpa notifikasi

---

## 2. Scope Fix

**Hanya 1 fungsi:** `_repair_supabase_if_missing`

Perubahan yang dilakukan:
- Ganti semua `print()` → `logger.error()` / `logger.warning()` / `logger.info()`
- Tambah `exc_info=True` pada error kritis agar stack trace ter-log
- Tidak mengubah logic atau flow fungsi

**Yang TIDAK diubah:**
- ❌ Tidak menambah retry logic (scope terpisah)
- ❌ Tidak mengubah flow `upload_queue`
- ❌ Tidak mengubah fungsi lain

---

## 3. Logger yang Digunakan

`ingestion.py` sudah punya logger di baris pertama:
```python
import logging
logging.basicConfig(level=logging.INFO)
```

Cukup tambahkan module-level logger:
```python
logger = logging.getLogger(__name__)
```

---

## 4. Before vs After

### Before
```python
def _repair_supabase_if_missing(...):
    try:
        from app.services.storage.supabase_client import check_supabase_hash, start_uploader_worker
    except Exception:
        print("Supabase client utilities not available; cannot repair Supabase.")
        return

    try:
        exists_in_supabase = check_supabase_hash(file_hash)
    except Exception as e:
        print(f"Error checking Supabase for hash {file_hash}: {e}")
        return

    if exists_in_supabase:
        return

    print(f"Neo4j node found but Supabase missing for {file_hash}. Attempting repair...")
    try:
        md = hybrid_parser.parse_pdf(pdf_path)
        if not md:
            print(f"Repair failed: unable to extract markdown from {pdf_path}")
            return

        chunks = generate_chunks(md, file_hash=file_hash, title=filename)
        if not chunks:
            print(f"Repair aborted: no chunks produced for {pdf_path}")
            return

        ...
        print(f"Repair completed: pushed {len(chunks)} chunks to Supabase for {file_hash}")

    except Exception as e:
        print(f"Exception during Supabase repair for {file_hash}: {e}")
        return
```

### After
```python
def _repair_supabase_if_missing(...):
    try:
        from app.services.storage.supabase_client import check_supabase_hash, start_uploader_worker
    except Exception:
        logger.error("Supabase client utilities not available; cannot repair Supabase.", exc_info=True)
        return

    try:
        exists_in_supabase = check_supabase_hash(file_hash)
    except Exception as e:
        logger.error(f"Error checking Supabase for hash {file_hash}: {e}", exc_info=True)
        return

    if exists_in_supabase:
        return

    logger.warning(f"Neo4j node found but Supabase missing for {file_hash}. Attempting repair...")
    try:
        md = hybrid_parser.parse_pdf(pdf_path)
        if not md:
            logger.error(f"Repair failed: unable to extract markdown from {pdf_path}")
            return

        chunks = generate_chunks(md, file_hash=file_hash, title=filename)
        if not chunks:
            logger.warning(f"Repair aborted: no chunks produced for {pdf_path}")
            return

        ...
        logger.info(f"Repair completed: pushed {len(chunks)} chunks to Supabase for {file_hash}")

    except Exception as e:
        logger.error(f"Exception during Supabase repair for {file_hash}: {e}", exc_info=True)
        return
```

---

## 5. Pemetaan Severity

| Kondisi | Before | After |
|---------|--------|-------|
| Import gagal | `print()` | `logger.error(..., exc_info=True)` |
| Hash check gagal | `print()` | `logger.error(..., exc_info=True)` |
| Repair dimulai | `print()` | `logger.warning()` |
| PDF parse gagal | `print()` | `logger.error()` |
| Tidak ada chunks | `print()` | `logger.warning()` |
| Repair selesai | `print()` | `logger.info()` |
| Exception umum | `print()` | `logger.error(..., exc_info=True)` |

---

## 6. Langkah Implementasi

| # | Langkah | Detail |
|---|---------|--------|
| 1 | Tambah module-level logger | `logger = logging.getLogger(__name__)` setelah `logging.basicConfig` |
| 2 | Ganti semua `print()` di `_repair_supabase_if_missing` | Sesuai pemetaan severity di atas |
| 3 | Verify syntax `py_compile` | Tidak ada syntax error |
| 4 | Smoke test | Verify logger terdefinisi dan fungsi bisa dipanggil |

---

## 7. Acceptance Criteria

- [ ] `logger = logging.getLogger(__name__)` ada di module level
- [ ] Tidak ada `print()` di dalam `_repair_supabase_if_missing`
- [ ] Error kritis menggunakan `logger.error(..., exc_info=True)`
- [ ] `python3 -m py_compile ingestion.py` → PASS
- [ ] Smoke test PASS

---

## 8. Estimasi Waktu

| Task | Waktu |
|------|-------|
| Tambah logger + ganti print() | 5 menit |
| Verify + smoke test | 5 menit |
| **Total** | **10 menit** |
