I read this as a forensic investigation - I’ll trace update `629525116` through read-only application, systemd, ingress, and PTB error paths, then report either the concrete exception or a persistence proof and minimal secure fix.


The failure is pinned to PID `343199` at `2026-08-17 08:47:23 +09:00`; I’m now correlating that journal event with the durable receipt schema and the exact live wheel/error-handler implementation, without touching the running process.


The durable artifacts intentionally preserve only `handler_exception`; the last pre-failure signal identifies the customer text path. I’m narrowing that path against the read-only customer fixture to determine whether the exception can be reproduced locally with all Telegram sends stubbed.


I read this as a forensic investigation - I traced the failure and proved the concrete exception was not persisted.

## Result

**The concrete exception behind update `629525116` is not recoverable from durable state.**

At `2026-08-17 08:47:23.433 KST`, PID `343199` recorded only:

```text
telegram_ingress stage=receipt update_id=629525116 reason_code=handler_exception
```

The immediately preceding event identifies `_handle_text_message`’s nutrition customer route, but neither the Telegram update payload nor the raised exception survived.

## Proof

- `agent.log`, `gateway.log`, and `errors.log` contain only the generic reason code—no exception class or traceback.
- Journald has exactly one event for this failure. Its `MESSAGE` field is the same generic line; there are no structured exception fields.
- The durable receipt contains only:

  ```json
  {
    "reason_code": "handler_exception",
    "stage": "blocked_receipt"
  }
  ```

- The receipt parser explicitly rejects any failure record other than that exact two-field object.
- `_handle_ptb_error` never reads `context.error`; it only calls `gate.failed(...)` and logs `handler_exception`.
- PTB 22.6 places the exception solely in the temporary `CallbackContext.error` passed to the registered handler. Since a custom error handler exists, PTB’s fallback traceback logger is not invoked.
- A safe synthetic invocation using `KeyError("sensitive-user-content")` confirmed `_handle_ptb_error` emits neither `KeyError` nor a traceback.
- `state.db` and its WAL contain no update ID or `handler_exception`.
- No update payload or separate failure receipt exists elsewhere in the profile.
- The process remains alive, there were no kernel events or core files around the failure, and this host lacks `coredumpctl`; this was a handled Python exception, not a crash.

A copied-profile reproduction could not reconstruct the exact handler execution because activation receipts are canonically bound to the live profile path. Running against the live profile would violate the read-only-state constraint.

## Observability defect

The custom PTB error handler deliberately discards the only object containing the exception:

```python
async def _handle_ptb_error(self, update, context):
    # context.error is ignored
    gate.failed(update, "handler_exception")
```

This creates durable offset-blocking evidence while irreversibly losing the failure class and stack.

## Smallest secure fix

In `_handle_ptb_error`, log:

- `update_id`
- fully qualified exception class
- traceback frame metadata only: filename/module, function, and line number

Do **not** use `logger.exception`, `exc_info`, `str(error)`, `repr(error)`, formatted `TracebackException`, locals, or the update object; those can include exception messages or user content.

Conceptually:

```python
error = getattr(context, "error", None)
if isinstance(error, BaseException):
    frames = [
        {
            "file": frame.f_code.co_filename,
            "function": frame.f_code.co_name,
            "line": lineno,
        }
        for frame, lineno in traceback.walk_tb(error.__traceback__)
    ]
    logger.error(
        "telegram_handler_exception update_id=%s exception_class=%s traceback=%s",
        getattr(update, "update_id", "unknown"),
        f"{type(error).__module__}.{type(error).__qualname__}",
        json.dumps(frames, separators=(",", ":")),
    )
```

Keep the existing bounded durable receipt unchanged. Add a unit test asserting class/frame presence and proving a secret embedded in the exception message and update content does not appear.

No service restart, network/provider call, Telegram update consumption, or live source/state/test/Git modification was performed.