Troubleshooting
This page maps common symptoms to their cause and fix. Each subsection is one symptom you can recognize from a log line, an exception, or a counter. Every behaviour here is a deliberate design choice — ScaiLog fails safe (never leaking PI, never silently dropping) rather than failing convenient, so most of these are signals telling you what to fix.
Logs appear on stderr as JSON instead of reaching the server#
Symptom. Your service prints ScaiLog entries as newline-delimited JSON to
stderr (landing in container or journald logs), and PI field values show up as
{"type": "...", "redacted": true} instead of their real value.
Cause. The SDK could not reach the local agent socket, or its in-process queue was full. Rather than drop the entry silently, the SDK falls back to stderr in the same NDJSON shape — and it redacts PI values first, keeping the field name and PI type so the drop is auditable but no plaintext leaks into stderr.
Fix. Make sure the agent (or a standalone server's embedded agent) is running
and that SCAILOG_SOCKET points at the socket it is listening on (default
/run/scailog/agent.sock). Confirm file permissions let your service connect.
Once the socket is reachable, entries flow over it again. A rising
dropped_to_stderr count is your signal that the agent is unavailable or your
emit rate is exceeding the queue.
SUBJECT_REQUIRED#
Symptom. An emit call raises ValueError: SUBJECT_REQUIRED: entry has PI markers but no subject= (Python), the entry is rejected with a SUBJECT_REQUIRED
diagnostic (TS/Go/.NET), or an ingested batch entry is nacked with
SUBJECT_REQUIRED.
Cause. The entry carries one or more pi() fields but no subject. Any PI
requires a data subject to attach it to — that's what makes cryptographic erasure
possible. The rule is enforced in the SDK before the entry leaves the process,
and again at the agent and server.
Fix. Pass the data-subject id on the emit call (subject=...), or set it on
the ambient context (e.g. withContext/WithSubject/LogContext.Push) so it
propagates automatically. If the field isn't actually about a natural person,
don't mark it pi().
invalid call_site_id#
Symptom. An emit call raises
ValueError: invalid call_site_id: '...' (pattern ^[A-Z0-9]+(-[A-Z0-9]+){2,}$),
or scailog-ci check fails the build on a call site.
Cause. Every entry needs a developer-assigned call_site_id, and it must
match ^[A-Z0-9]+(-[A-Z0-9]+){2,}$ — at least three uppercase-alphanumeric
segments joined by hyphens, e.g. SCAI-CHECKOUT-PAID-001. Call-site provenance
is first-class in ScaiLog (GDPR remediation is "fix one call site"), so the id
can't be blank, lowercase, or too few segments. The AUTO- and SCAILOG-
prefixes are reserved for the system.
Fix. Give the call site a constant, unique, well-formed id. Run scailog-ci check in CI to catch bad or interpolated ids before they ship — it also enforces
that the id is a constant (not built from a variable) and that pi() is
accompanied by a subject.
Entries turn into [PANIC_DROPPED:...] (SCAILOG-AGENT-PANIC-DROP-001)#
Symptom. A PI field is stored as [PANIC_DROPPED:<pi_type>] in the entry
metadata, the agent's panic_drops counter rises, and the agent emits a warning
under call site SCAILOG-AGENT-PANIC-DROP-001: "refused to cache plaintext PI:
server unreachable and no DEK available".
Cause. The agent needs a per-subject DEK to encrypt PI, and it couldn't get one — the server was unreachable (so enrollment or DEK fetch failed), or the subject was erased (the server returns HTTP 410, the key is gone). Rather than write plaintext PI to its local cache, the agent enters panic mode and drops the value, loudly.
Fix. Restore connectivity to the server and confirm the agent's API key is
valid, then new entries encrypt and ship normally. If the drops are for an
erased subject, that is correct behaviour — the key was destroyed by design
and cannot come back. Never reach for --fail-open in production to make the
drops stop: it caches plaintext PI and exists only for dev.
A console script prints pip install 'scailog[...]' and exits#
Symptom. Running scailog, scailog-server, scailog-agent, or
scailog-audit exits immediately with, e.g.:
1 2 3 | |
Cause. The base install is the pure-stdlib SDK, which carries none of the
server/agent/CLI/audit dependencies. The console scripts are wrapped so a missing
extra becomes a one-line install hint instead of a raw ModuleNotFoundError
traceback.
Fix. Install the extra named in the hint, e.g. pip install 'scailog[server]'
(or agent, cli, audit, or all). Note scailog-ci is the exception — it is
zero-dependency and runs on the base install. (If the missing dependency is a
scailog.* submodule rather than a third-party package, that's a genuine bug and
is re-raised untouched — it won't be masked by an install hint.)
RESERVED_FIELD rejection#
Symptom. An emit call raises ValueError: reserved field name: '...'
(Python) or the entry is rejected/nacked with RESERVED_FIELD.
Cause. A field name collides with a reserved top-level entry key. These names
are reserved: event_id, tenant, service, env, site, level, ts,
msg, fields, pi, subject, trace_id, request_id. Allowing them as
user fields would let a field shadow a protocol key.
Fix. Rename the field to something outside that set (e.g. use user_service
instead of service). The check runs on both plain fields and pi() fields.
PI never gets encrypted — it's kept or dropped instead#
Symptom. A field you marked pi() shows up in plaintext fields (or is
gone) rather than encrypted, and the entry metadata records an action of keep,
redact, or drop.
Cause. The policy engine decides the action per PI type. Not every type
defaults to encrypt: pseudonymous defaults to keep, and credentials and
special_category default to drop (credentials and special-category data are
dropped, not stored encrypted). Your policy may also override the default for a
service or environment.
Fix. Check the effective policy for the service/env/PI-type combination
(scailog explain / the policies view). If a type should be encrypted, set an
encrypt action for it in your policy. If you marked the wrong PI type, fix the
marker at the call site.