# Debugging Guide

How to troubleshoot issues with the alphainfo SDK.

---

## Quick diagnostic checklist

1. Can you reach the API? → `client.health()`
2. Is your key valid? → Try `client.health()` first (no auth needed), then `client.plans()`
3. Is the signal valid? → Check length (>= 10), values (finite floats), and sampling_rate (> 0)
4. Are you getting unexpected scores? → Check domain, sampling_rate, and signal length

---

## Problem: "Invalid or missing API key" (AuthError, 401)

**Causes:**
- API key not set or empty string
- Key doesn't start with `ai_`
- Key was revoked or expired
- Using a key from a different environment (dev vs prod)

**Debug steps:**

```python
# 1. Print key prefix (never print full key)
print(f"Key starts with: {api_key[:6]}...")
print(f"Key length: {len(api_key)}")

# 2. Health check (no auth) to verify connectivity
health = client.health()
print(f"API is reachable: {health.status}")

# 3. Try plans endpoint (requires valid key)
try:
    plans = client.plans()
    print("Key is valid!")
except AuthError:
    print("Key is invalid")
```

---

## Problem: ValidationError (400/413)

**Common messages and fixes:**

| Message | Cause | Fix |
|---------|-------|-----|
| `signal cannot be empty` | Empty list | Send at least 10 samples |
| `sampling_rate must be positive` | Zero or negative rate | Use actual Hz (e.g., 250.0 for ECG) |
| `Signal too short` | < 10 samples after server validation | Send more data |
| `Payload too large` (413) | Signal exceeds size limit | Reduce signal length or batch size |
| `baselines length ... must match` | Baselines count != signals count | Match array lengths |
| `Maximum 100 signals per batch` | > 100 signals in batch | Split into multiple batches |
| `Maximum 50 signals per matrix` | > 50 signals in matrix | Reduce signal count |

**Debugging signal content:**

```python
import math

# Check for NaN/Inf
has_nan = any(math.isnan(x) for x in signal)
has_inf = any(math.isinf(x) for x in signal)
print(f"Length: {len(signal)}, NaN: {has_nan}, Inf: {has_inf}")
print(f"Range: [{min(signal):.4f}, {max(signal):.4f}]")
print(f"All same value: {len(set(signal)) == 1}")
```

---

## Problem: RateLimitError (429)

**What it means:** You've hit your monthly quota or concurrent request limit.

```python
from alphainfo.exceptions import RateLimitError

try:
    result = client.analyze(...)
except RateLimitError as e:
    print(f"Rate limited: {e.message}")
    print(f"Retry after: {e.retry_after} seconds")
    print(f"Response: {e.response_data}")
```

**Check remaining quota:**

```python
result = client.analyze(signal=data, sampling_rate=250.0)
info = client.rate_limit_info
if info:
    print(f"Remaining: {info.remaining}/{info.limit}")
    print(f"Resets at: {info.reset}")
```

**Solutions:**
- Upgrade your plan at [alphainfo.io/dashboard/billing](https://alphainfo.io/dashboard/billing)
- Use `analyze_batch()` to process multiple signals in fewer requests
- Use `analyze_vector()` for multi-channel data (counts as 1 analysis)
- Cache results — same signal produces the same score

---

## Problem: TimeoutError

**What it means:** The request timed out after all retry attempts.

```python
from alphainfo.exceptions import TimeoutError

try:
    result = client.analyze(signal=data, sampling_rate=250.0)
except TimeoutError as e:
    print(f"Timed out: {e.message}")
```

**Common causes:**
1. **Large signal with multiscale** — Multiscale analysis takes 20-30s. Default timeout is 60s for analyze, but very large signals may exceed it.
2. **Network issues** — Slow connection, proxy, firewall.
3. **Server load** — Rare, check `client.health()`.

**Solutions:**

```python
# Increase timeout for large signals
client = AlphaInfo(api_key="ai_xxx", timeout=120.0)

# Or use fast mode for initial check
result = client.analyze(signal=data, sampling_rate=250.0, use_multiscale=False)

# More retries with longer delays
client = AlphaInfo(
    api_key="ai_xxx",
    max_retries=5,
    retry_base_delay=2.0,
    retry_max_delay=60.0,
)
```

---

## Problem: Unexpected structural_score

### Score is always 0.5

**Cause:** Signal is too short (10-99 samples). The API returns 0.5 as a "no-confidence" score with a warning.

```python
result = client.analyze(signal=short_data, sampling_rate=250.0)
print(result.warning)  # "Signal has only 30 samples..."
```

**Fix:** Send 200+ samples.

### Score doesn't change between very different signals

**Possible causes:**
1. **Wrong sampling_rate** — If rate is way off, window sizes are wrong
2. **Wrong domain** — Domain calibration shifts score interpretation
3. **No baseline** — Without a baseline, the API compares internal windows. Provide an explicit baseline for comparison:

```python
result = client.analyze(
    signal=new_data,
    sampling_rate=250.0,
    baseline=reference_data,  # "normal" period for comparison
)
```

### Score seems inverted

**Note:** `structural_score` is a **similarity** score (0 = different, 1 = identical). It's NOT a change magnitude. So:
- High score (0.8-1.0) = signal is **stable** / similar to baseline
- Low score (0.0-0.3) = signal has **diverged** significantly
- `change_detected=True` typically when score drops below the domain threshold

---

## Problem: Semantic layer returns None

**Cause:** `include_semantic` defaults to `False`.

```python
# Bad — no semantic
result = client.analyze(signal=data, sampling_rate=250.0)
print(result.semantic)  # None

# Good — request semantic
result = client.analyze(
    signal=data, sampling_rate=250.0,
    include_semantic=True,
)
print(result.semantic.alert_level)  # 'normal', 'attention', etc.
```

---

## Problem: NetworkError after all retries

**What it means:** The SDK tried `max_retries + 1` times and failed every attempt.

```python
from alphainfo.exceptions import NetworkError

try:
    result = client.analyze(...)
except NetworkError as e:
    print(f"All retries exhausted: {e.message}")
```

**Debug steps:**

```python
# 1. Test basic connectivity
try:
    health = client.health()
    print(f"API reachable: {health.status}")
except Exception as e:
    print(f"Cannot reach API: {e}")

# 2. Test with longer timeout
client_debug = AlphaInfo(api_key="ai_xxx", timeout=120.0, max_retries=0)
try:
    result = client_debug.analyze(signal=[1.0]*200, sampling_rate=1.0)
except Exception as e:
    print(f"Single attempt: {type(e).__name__}: {e}")

# 3. Check if it's DNS/proxy
import httpx
try:
    r = httpx.get("https://www.alphainfo.io/health", timeout=10.0)
    print(f"Direct httpx: {r.status_code} {r.json()}")
except Exception as e:
    print(f"Direct httpx failed: {e}")
```

---

## Enabling verbose logging

The SDK uses `httpx` under the hood. Enable its logging to see request/response details:

```python
import logging

# See all HTTP traffic
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("httpx").setLevel(logging.DEBUG)
logging.getLogger("httpcore").setLevel(logging.DEBUG)

client = AlphaInfo(api_key="ai_xxx")
result = client.analyze(signal=data, sampling_rate=250.0)
```

This shows:
- Request URL, method, headers
- Response status, headers, timing
- Retry attempts
- Connection reuse (especially useful with HTTP/2)

**For production, log only warnings:**

```python
logging.getLogger("httpx").setLevel(logging.WARNING)
```

---

## Inspecting the raw response

If you need to see what the API actually returned (e.g., to report a bug):

```python
import httpx

# Make a raw request outside the SDK
raw = httpx.post(
    "https://www.alphainfo.io/v1/analyze/stream",
    json={
        "signal": data,
        "sampling_rate": 250.0,
        "domain": "generic",
    },
    headers={"X-API-Key": "ai_xxx"},
    timeout=60.0,
)
print(f"Status: {raw.status_code}")
print(f"Headers: {dict(raw.headers)}")
print(f"Body: {raw.json()}")
```

---

## Getting help

If you're stuck:

1. Check the [API docs](https://alphainfo.io/docs) (Swagger UI)
2. Review the [common mistakes](COMMON_MISTAKES.md)
3. Contact support: **contato@alphainfo.io**
