Valkyrian Labs logo

Troubleshooting

Nothing happens, Keychain prompts, Pi-hole login and version errors, approvals and stale plans, devices that do not renew, shadowing Pi-hole entries, DNS bypass and forti-piholed refusals.

Troubleshooting

Start with forti-pihole status (enabled, paused, profile and mode, a plan waiting for approval, open operations) and forti-pihole plan (what a sync would do, and the notes that explain why something does not). Every error names what failed; none contains a secret.

Nothing happens

SymptomCauseFix
sync ends with nothing written: the configuration is not enabled and exits 7"enabled": falseReview plan, then set "enabled": true
nothing written: sync is paused (forti-pihole resume)forti-pihole pauseforti-pihole resume
Moves appear in plan but no reservation is ever added"manage_reservations": false on that networkSet it to true, or move one device with forti-pihole migrate MAC
forti-piholed logs runs but nothing changesNot enabled or paused (it reads both before every run)As above
A device is never classifiedIts interface is not in interfaces (by default: the networks' interfaces)Add the network
another forti-pihole run is in progress (exit 3)The timer, the service or another shell holds the run lockWait; runs are short
A group shows [NOT MANAGED: name clash]An address group fp-grp-<group> exists without the markerRename your object, or the group

macOS asks for the Keychain again

Each Keychain item belongs to the binary that created it. The first time another binary reads it, and after every upgrade of forti-pihole or forti-piholed (a new binary), macOS asks whether it may. Choose Always Allow. forti-pihole and forti-piholed are two binaries, so each asks once per item (the FortiGate API key, the Pi-hole app password, the webhook token).

Without a GUI session (SSH, brew services, launchd) macOS cannot show that prompt, and the run fails with a configuration error. Run the program once in a terminal after installing or upgrading (forti-pihole plan, and forti-piholed until its first run has finished, then Ctrl-C), or unlock the keychain (security unlock-keychain). FORTI_SECRET_STORE=file selects the engine's file store instead; every secret must then be stored there (forti configure, forti-pihole setup, forti-pihole events install, run with the same setting).

Pi-hole login and version errors

Errors read Pi-hole at <url>: .... The common ones:

Message containsMeaningFix
no Pi-hole password stored for <name>; run forti-pihole setupNo app password in the secret storeforti-pihole setup
the stored Pi-hole password belongs to <host:port>, not <host:port>pihole.url now points elsewhere than when the password was storedforti-pihole setup stores one for the new URL
password incorrect; check the app password (no retry: FTL rate-limits logins)Wrong or revoked app passwordCreate a new app password, then forti-pihole setup
2FA is enabled: configure an app password insteadThe admin password was used, and two-factor authentication is onUse an app password
HTTP 429FTL's login rate limit, or no free API session (webserver.api.max_sessions, default 16)Wait; check for other API clients holding sessions
FTL <version> is older than 6.7.0An FTL older than forti-pihole supportsUpdate Pi-hole
cannot tell the FTL versionA development or custom build reports no usable versionRun a released FTL
plain http:// is accepted for loopback hosts onlyAn http:// URL to another hostUse https://
A TLS verification errorPi-hole's certificate is not trustedPoint pihole.ca_bundle at your CA's PEM file

During a sync, a Pi-hole error skips the Pi-hole stage (reported on standard error as pihole: ...) and the FortiGate stages still run.

Waiting for approval

A guarded forti profile applies nothing until you approve the exact plan:

1objects       waiting   waiting for approval: forti approve 20261017-101500-ab12cd

The exit status is 7. Run forti approve <plan id> (it shows the change and asks), then:

  • FortiGate objects (objects stage): run forti-pihole sync again; it applies the approved plan first. A pending plan older than a day is planned again from fresh reads.
  • A migration step: forti-pihole operations resume ID, or the next sync, applies the same plan.
  • events install: forti apply <plan id>.

the forti profile is read-only: nothing applied means the profile's mode forbids changes; changing the mode is a decision for forti, not for forti-pihole.

stale_plan

stale_plan means the FortiGate changed between planning and applying, so the engine refused the plan rather than apply it to a configuration it was not made for. For the FortiGate objects, the next sync simply plans again. For a migration step that failed with stale_plan, roll the operation back (forti-pihole operations rollback ID); the next sync, or forti-pihole migrate MAC, plans a fresh migration from current reads.

A device does not move into its slice

OutputMeaning
observe: the device has not taken <new> (it has <old>)It has not renewed its lease. Reconnect its Wi-Fi or power-cycle it, then forti-pihole operations resume ID (or wait for the next sync)
migration refused: ... is privileged by addressA host object on the old address is a policy source: forti-pihole adopt MAC --object NAME
migration refused: ...'s reservation <id> is an administrator'sforti-pihole adopt MAC --reservation
migration refused: VIP <name> forwards to <old>A port forward follows the address: move or remove the VIP first
migration refused: target <ip> is in useSomething holds the chosen address; forti-pihole references <ip> shows what
note (slice-full)No free address in the group's slices: see capacity
note (no-address)No current address known; it waits for a lease
verify failed: lost DNS reach or lost internet reachYour policies treat the new address differently. Fix the policy, then operations resume ID, or operations rollback ID
verify failed: Pi-hole resolves <ip> to groups [...]An entry of yours decides that address (below)

A device with a randomized MAC that rotates gets a new identity, and its reservation stays with the old one: see Randomized MACs.

Pi-hole applies the wrong group

Pi-hole uses the most specific entry that matches an address: an exact-IP entry beats every CIDR, and a narrower CIDR beats a wider one (how Pi-hole resolves clients). plan reports what decides instead:

  • note (unmanaged-shadow): one of your IP or narrower-CIDR entries lies inside a managed range. Once the device's new address is verified, remove your entry (forti-pihole never removes it).
  • note (resolution): Pi-hole resolves the managed range to other groups than intended.
  • note (mac-client): an entry by MAC. Pi-hole cannot see the MAC of clients routed through the FortiGate, and any address or subnet entry covering the device takes precedence over it anyway.
  • note (missing-group): the mapped Pi-hole group does not exist; create it in Pi-hole.

If Pi-hole's query log shows the FortiGate (or a single address) as the client for everything, Pi-hole cannot tell devices apart at all: the clients use the FortiGate's DNS proxy, or the policy to Pi-hole applies source NAT. See Pi-hole must see the real client address.

A device is not filtered at all

Its queries do not reach Pi-hole: DNS over HTTPS or TLS, iCloud Private Relay, a hard-coded resolver or a VPN. That is outside what DNS filtering can see; block it with FortiGate policies. A DNS redirect VIP to Pi-hole only takes effect through a policy that uses it. See DNS that bypasses Pi-hole.

forti-piholed refuses events

forti-piholed --check-config shows the listener, the allowlist and whether a token is stored.

AnswerCause
403The sender is not in allow_sources (by default the profile FortiGate's resolved address)
503No webhook token stored: run forti-pihole events install
401The token in the FortiGate's webhook action is not the stored one (for example, token_account names another account)
429The per-source rate limit
No connection at allIt listens on 127.0.0.1: set listen.address to the management address