# Error Handling Mechanisms in main.py: How qiaomu-anything-to-notebooklm Manages Failures

> Discover error handling in qiaomu-anything-to-notebooklm's main.py. Learn about return code validation, exception catching, retries, and graceful exits. Ensure clear failure reporting.

- Repository: [向阳乔木/qiaomu-anything-to-notebooklm](https://github.com/joeseesun/qiaomu-anything-to-notebooklm)
- Tags: how-to-guide
- Published: 2026-05-16

---

**The qiaomu-anything-to-notebooklm tool implements robust error handling in [`main.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/main.py) through subprocess return-code validation, explicit exception catching, retry loops with back-off, and graceful exits via `sys.exit(1)` to ensure clear failure reporting when external commands fail.**

[`main.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/main.py) serves as the entry point for the **qiaomu-anything-to-notebooklm** project, orchestrating interactions with external commands like `notebooklm` and `lark-cli` while parsing JSON and handling network operations. Understanding the error handling mechanisms in [`main.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/main.py) is essential for debugging integration failures with NotebookLM and Feishu workflows. The codebase employs a fail-fast strategy that validates subprocess results, catches specific exceptions, and aborts with descriptive messages when recovery is impossible.

## Subprocess Return-Code Validation

### Validating External Command Execution

After every `subprocess.run` call, the code explicitly checks `result.returncode` to detect failures before proceeding. This pattern appears in `upload_to_notebooklm` (lines 71-84) and `create_feishu_doc` (lines 25-38), where non-zero return codes trigger immediate error reporting to stderr.

```python
result = subprocess.run(
    ['notebooklm', 'create', title],
    capture_output=True,
    text=True
)
if result.returncode != 0:
    print(f"❌ 创建笔记本失败: {result.stderr}", file=sys.stderr)
    return False

```

## Graceful Early Exit Strategy

### Immediate Termination on Critical Failures

When essential steps fail—such as notebook creation, transcript downloads, or JSON parsing—the script prints detailed error messages and calls `sys.exit(1)` to halt execution. This pattern appears throughout `main()` when a `notebooklm` call fails (lines 80-83), after a failed transcript fetch (lines 75-78), and when detecting unsupported input types (lines 104-106).

```python
else:
    print(f"❌ 不支持的输入类型: {input_type}", file=sys.stderr)
    print("提示: 请使用 EPUB、PDF、TXT、MD 文件或 URL", file=sys.stderr)
    sys.exit(1)

```

## Retry Mechanisms with Back-off

### Resilient Query Handling in ask_notebooklm

The `ask_notebooklm` function implements a retry loop (lines 84-103) that catches non-zero return codes and short answers, attempting recovery up to a configurable maximum (default 1 retry). Between attempts, the code sleeps for 2 seconds (lines 99-101) to prevent rapid-fire failures.

```python
def ask_notebooklm(question, max_retries=1):
    for attempt in range(max_retries + 1):
        result = subprocess.run(
            ['notebooklm', 'ask', question],
            capture_output=True,
            text=True
        )
        if result.returncode == 0 and result.stdout.strip():
            return result.stdout.strip()
        if attempt < max_retries:
            print("  重试中...", end=" ")
            time.sleep(2)          # simple back‑off

    print(f"⚠️ 提问失败（已重试{max_retries}次）", file=sys.stderr)
    return None

```

## JSON Decoding Safety

### Protected JSON Parsing

When processing transcript data, the output is wrapped in a `try/except json.JSONDecodeError` block (lines 381-384). If parsing fails, the raw output is logged to stderr before the script aborts, providing debug context without crashing silently.

```python
try:
    data = json.loads(result.stdout.strip())
except json.JSONDecodeError:
    print(f"❌ 解析输出失败: {result.stdout}", file=sys.stderr)
    sys.exit(1)

```

## Input Validation and File Safety

### Proactive File Existence Checks

The `detect_input_type` function uses `Path(input_path).exists()` (lines 30-33) to verify file availability before operations, preventing `FileNotFoundError` exceptions during processing.

### Input Type Validation

The script validates input types (EPUB, PDF, TXT, MD, URL) and prints user-friendly errors when encountering unsupported formats (lines 104-106).

### Filename Sanitization

When creating temporary files for X/Twitter posts, the code uses regular expressions to strip unsafe characters (lines 29-32), preventing path-related injection vulnerabilities.

## Error Handling Patterns in Related Modules

While [`main.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/main.py) contains the primary logic, similar patterns appear in supporting files according to the repository structure. The [`check_env.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/check_env.py) script demonstrates environment-related validation, while [`scripts/get_podcast_transcript.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/scripts/get_podcast_transcript.py) handles network failures that propagate to the main flow. The Feishu integration module in `feishu-read-mcp/` mirrors the subprocess return-code approach used for `lark-cli` calls.

## Summary

- **Subprocess return-code checks** after every external command execution ensure immediate failure detection in functions like `upload_to_notebooklm`.
- **Graceful exits** via `sys.exit(1)` prevent partial state when critical operations fail, with clear error messages printed to stderr.
- **Retry loops** with 2-second delays provide resilience for NotebookLM queries in `ask_notebooklm`.
- **Try/except blocks** around `json.JSONDecodeError` protect against malformed API responses from transcript scripts.
- **Proactive file existence checks** and input validation prevent common runtime errors before file operations begin.
- **Regex-based filename sanitization** blocks path traversal attempts when handling social media content.

## Frequently Asked Questions

### What happens when the notebooklm command fails?

When the `notebooklm` command returns a non-zero exit code in `upload_to_notebooklm` (lines 71-84), the script prints the stderr output with a clear error message and returns `False`. If this occurs during the main workflow, the script subsequently calls `sys.exit(1)` to terminate execution rather than continuing with an invalid state.

### How does main.py handle invalid JSON responses?

The code wraps JSON parsing in a try/except block that catches `json.JSONDecodeError` (lines 381-384). When triggered, it prints the raw stdout content to stderr and exits with status 1, allowing users to see exactly what malformed data was received from external scripts like [`get_podcast_transcript.py`](https://github.com/joeseesun/qiaomu-anything-to-notebooklm/blob/main/get_podcast_transcript.py).

### Why does the script use sys.exit(1) instead of raising exceptions?

Using `sys.exit(1)` provides immediate, predictable termination with a non-zero status code that signals failure to calling processes and CI/CD pipelines. This approach ensures the tool fails loudly with explanatory messages printed to stderr, rather than propagating uncaught exceptions or returning incomplete results.

### How many retry attempts does ask_notebooklm perform by default?

The `ask_notebooklm` function defaults to **1 retry attempt** (for a total of 2 tries), with a 2-second sleep delay between attempts (lines 99-101). This default balances resilience against temporary network issues with avoiding excessive delays in the user workflow.