# Known Issues

This page lists confirmed defects in the v1.0.0 source that make behavior differ from the design, and what to do until they are fixed.

## Security and Availability

| Issue | Location | Impact | Workaround |
|---|---|---|---|
| Nil pointer panic when Redis fails during scoring | `instance.go:156` | `dynamicScore` returns `nil, err`; `Check` only logs the error, then reads `score.IsBlock` | Use `gin.Recovery()` with Gin; `net/http` drops that connection |
| Proxy headers are trusted for the client IP | `device.go` `getClientIP` | Forging `X-Forwarded-For` and similar headers bypasses rate limits, Block, and Deny, or impersonates an Allow-listed IP | Have the front proxy overwrite or strip the headers; see [Client IP Resolution](/client-ip) |
| Blocks become permanent when `BlockTimeMin` / `BlockTimeMax` is 0 | `block.go` `Add` | Unset values make block records never expire | Set both to positive values |

## Designed Features That Never Run

| Feature | Location | Reason |
|---|---|---|
| `HighRiskCountry` scoring | `geo.go:248` | History parsing stores the country code in `Country`, but the comparison reads `CountryCode`, which is always empty |
| Automatic `Block.Add` on a high score | `score.go:196` | The condition is `> 100`, but `calcScore` already caps the total at 100 |
| Escalating to Deny after `BlockToBan` requests while blocked | `instance.go:141` | `device.Is.Block` returns `403` one step earlier, so the check never runs |
| `too_frequent_requests` | `score.go:546` | Needs 16 intervals while the list keeps at most 10; `ScoreFrequencyRequest` also has no default |
| `ScoreNormal` | `type.go` | The field exists but nothing reads it |

## Other Behavior Defects

| Issue | Location | Impact |
|---|---|---|
| Email CC is never delivered | `deny.go:157` | `smtp.SendMail` receives only `To` as recipients; `CC` appears only in the header |
| Geo detection off when only `CountryDB` is set | `score.go:441` | `calcGeo` requires `CityDB` |
| Debug string printed on every request | `score.go:457` | With GeoLite2 on, the location and key go through the standard `log.Print`, bypassing `Logger` |
| Defaults written back into `Config` on the request path | `instance.go`, `score.go`, `geo.go` | Concurrent requests write the same fields, which `go test -race` reports as a data race |
| False rapid-move flag when alternating private and public IPs | `geo.go` | Private records sit at (0, 0); see [Geo Detection](/geo-detection) |
| `LoginFailure` / `NotFound404` count the request twice | `score.go` | Both run device identification again, so one request counts twice per minute |

## Reproduction: Panic on Redis Outage

```go
package main

import (
	"fmt"
	"net/http/httptest"
	"strconv"
	"time"

	"github.com/alicebob/miniredis/v2"
	golangIPSentry "github.com/pardnchiu/golang-ip-sentry"
)

func main() {
	mr, err := miniredis.Run()
	if err != nil {
		panic(err)
	}
	port, _ := strconv.Atoi(mr.Port())
	sentry, err := golangIPSentry.New(golangIPSentry.Config{
		Redis:     golangIPSentry.Redis{Host: mr.Host(), Port: port},
		Parameter: golangIPSentry.Parameter{BlockTimeMin: time.Minute, BlockTimeMax: time.Hour},
	})
	if err != nil {
		panic(err)
	}
	mr.Close()

	defer func() { fmt.Println("recovered:", recover()) }()
	r := httptest.NewRequest("GET", "https://example.com/", nil)
	sentry.Check(r, httptest.NewRecorder())
}
```

Actual output:

```
recovered: runtime error: invalid memory address or nil pointer dereference
```
