Skip to content

core.crash

Top-level crash handler that writes a report file on unhandled exceptions.

Crash reports include environment info and sanitized log tail so the user can attach them to GitHub issue reports without manual data gathering.

Crash file contents

Each crash report contains:

  • UTC timestamp
  • Exception type, message, and full traceback
  • taskclf version, Python version, and OS
  • Last N sanitized log lines (if log file exists)
  • Link to the GitHub issue tracker

Crash file location

Files are written to <TASKCLF_HOME>/logs/crash_<YYYYMMDD_HHMMSS>.txt.

Platform Path
macOS ~/Library/Application Support/taskclf/logs/crash_*.txt
Linux ~/.local/share/taskclf/logs/crash_*.txt
Windows %LOCALAPPDATA%/taskclf/logs/crash_*.txt

Privacy

  • Log tail lines are sanitized via redact_message() before inclusion.
  • Tracebacks contain only source code references, not data payloads.
  • No automatic telemetry — the user controls what they submit.

taskclf.core.crash

Top-level crash handler that writes a report file on unhandled exceptions.

Crash reports include environment info and sanitized log tail so the user can attach them to GitHub issue reports without manual data gathering.

write_crash_report(exc, *, log_dir=None, log_tail_lines=20)

Write a crash report to <log_dir>/crash_<YYYYMMDD_HHMMSS>.txt.

Parameters:

Name Type Description Default
exc BaseException

The unhandled exception.

required
log_dir Path | None

Directory for the crash file. Defaults to <TASKCLF_HOME>/logs.

None
log_tail_lines int

Number of log-file lines to append.

20

Returns:

Type Description
Path

Absolute path to the crash report file.

Source code in src/taskclf/core/crash.py
def write_crash_report(
    exc: BaseException,
    *,
    log_dir: Path | None = None,
    log_tail_lines: int = 20,
) -> Path:
    """Write a crash report to ``<log_dir>/crash_<YYYYMMDD_HHMMSS>.txt``.

    Args:
        exc: The unhandled exception.
        log_dir: Directory for the crash file.  Defaults to
            ``<TASKCLF_HOME>/logs``.
        log_tail_lines: Number of log-file lines to append.

    Returns:
        Absolute path to the crash report file.
    """
    if log_dir is None:
        from taskclf.core.paths import taskclf_home

        log_dir = taskclf_home() / "logs"

    log_dir = Path(log_dir)
    log_dir.mkdir(parents=True, exist_ok=True)

    now = datetime.now(timezone.utc)
    timestamp_slug = now.strftime("%Y%m%d_%H%M%S")
    crash_file = log_dir / f"crash_{timestamp_slug}.txt"

    # Avoid overwriting if two crashes land in the same second
    counter = 1
    while crash_file.exists():
        crash_file = log_dir / f"crash_{timestamp_slug}_{counter}.txt"
        counter += 1

    tb_text = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
    version = _get_version()
    os_info = f"{platform.system()} {platform.release()} ({platform.machine()})"
    log_tail = _read_log_tail(log_dir, log_tail_lines)

    sections = [
        "taskclf crash report",
        "=" * 40,
        f"Timestamp : {now.isoformat()}",
        f"Version   : taskclf {version}",
        f"Python    : {sys.version}",
        f"OS        : {os_info}",
        "",
        "Exception",
        "-" * 40,
        f"Type    : {type(exc).__qualname__}",
        f"Message : {exc}",
        "",
        "Traceback",
        "-" * 40,
        tb_text.rstrip(),
    ]

    if log_tail:
        sections += [
            "",
            f"Last {len(log_tail)} log lines",
            "-" * 40,
            *log_tail,
        ]

    sections += [
        "",
        "=" * 40,
        f"Please report this issue at:\n  {ISSUE_URL}",
    ]

    crash_file.write_text("\n".join(sections) + "\n", "utf-8")
    return crash_file