[Bug]: Same PDF gives different markdown on each run when batch_size > 1 (threads share current_page_number)
Los mantenedores suelen responder en 3 días
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 84/100
Línea de trabajo
Empieza en processors/pdf/processor.py, en NaivePDFProcessorStrategy.process_batch(), _process_page() y _extract_images(); revisa cómo se utiliza la current_page_number compartida. Ejecuta repro.py con batch_size=4 y batch_size=1, y verifica que las ejecuciones repetidas sean deterministas, que el formato de la página 1 se mantenga en la página 1 y que los números de página y los nombres de las imágenes sigan siendo correctos.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
crawl4ai version
0.9.0 (also reproduced with processors/pdf/processor.py and utils.py from v0.9.4; the code involved is unchanged on main)
Expected Behavior
This report was drafted with the help of an AI assistant. The reproduction below was run locally.
Processing the same PDF twice should give the same markdown, and the page-1-only formatting in clean_pdf_text() should only ever apply to page 1.
Current Behavior
With the default batch_size=4 (also the default of PDFContentScrapingStrategy), the output changes from run to run for the same file. In the repro below, a Title Case line that clean_pdf_text() treats as an author line (which it only does on page 1) ends up bolded on page 1, on no page at all, or on other pages, giving 5 to 7 distinct outputs over 30 runs. With batch_size=1 the output is always the same. We noticed because we hash the extracted text to detect changed documents, and unchanged PDFs looked changed on every crawl.
The cause is in NaivePDFProcessorStrategy.process_batch(). Each worker writes its page number to the shared processor instance, and _process_page() reads it back after text extraction, by which time another worker may have overwritten it:
def process_page_safely(page_num: int):
...
self.current_page_number = page_num + 1
return self._process_page(page, image_dir)
# _process_page()
pdf_page.markdown = clean_pdf_text(self.current_page_number, pdf_page.raw_text)
pdf_page.html = clean_pdf_text_to_html(self.current_page_number, pdf_page.raw_text)
The same attribute also feeds PDFPage.page_number and the extracted image file names (page_{current_page_number}_img_{n}).
Suggested fix: pass the page number down instead of storing it on self, i.e. self._process_page(page, image_dir, page_num + 1), and use that argument in _process_page() and _extract_images(). With that change the repro gives a single output for batch_size=4 as well.
Workaround: PDFContentScrapingStrategy(batch_size=1).
Is this reproducible?
Yes
Inputs Causing the Bug
- URL(s): none, the script below generates a local 12-page PDF
- Settings used: NaivePDFProcessorStrategy(batch_size=4), the default, vs batch_size=1
- Input data: each page has a Title Case line that clean_pdf_text() only bolds on page 1
Steps to Reproduce
1. pip install "crawl4ai[pdf]" fpdf2
2. Save the code snippet below as repro.py
3. python repro.py
4. Compare the batch_size=4 and batch_size=1 results
Code snippets
# pip install "crawl4ai[pdf]" fpdf2
from collections import Counter
from pathlib import Path
from fpdf import FPDF
from crawl4ai.processors.pdf.processor import NaivePDFProcessorStrategy
# 12 pages, each with a Title Case line that clean_pdf_text() only bolds on page 1.
path = Path("race.pdf")
pdf = FPDF()
pdf.set_font("Helvetica", size=12)
for n in range(1, 13):
pdf.add_page()
pdf.cell(0, 10, f"Chapter Heading Number {n}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 10, "Assessment Overview", new_x="LMARGIN", new_y="NEXT")
pdf.multi_cell(0, 8, f"this is the body of page {n}. " * 60)
pdf.output(str(path))
for batch_size in (4, 1):
outputs = Counter()
for _ in range(30):
result = NaivePDFProcessorStrategy(batch_size=batch_size).process_batch(path)
bolded = tuple(
number
for number, page in enumerate(result.pages, start=1)
if "**Assessment Overview**" in page.markdown
)
outputs[bolded] += 1
print(f"batch_size={batch_size}: {len(outputs)} distinct outputs over 30 runs")
for bolded, runs in outputs.most_common():
print(f" {runs:2d} runs: bolded on pages {list(bolded)}")
OS
Linux (Arch Linux)
Python version
3.12.12
Browser
No response
Browser version
No response
Error logs & Screenshots (if applicable)
Output of repro.py on 0.9.0:
batch_size=4: 7 distinct outputs over 30 runs
18 runs: bolded on pages [1]
7 runs: bolded on pages []
1 runs: bolded on pages [4]
1 runs: bolded on pages [5]
1 runs: bolded on pages [1, 3]
1 runs: bolded on pages [1, 2]
1 runs: bolded on pages [2]
batch_size=1: 1 distinct outputs over 30 runs
30 runs: bolded on pages [1]
- Lenguaje dominante
- Python
- Estrellas
- 85.9k
- Forks
- 8.9k
- Merge medio
- 5 d 6 h
- PR fusionados (30 d)
- 24
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de unclecode/crawl4ai
-
[Bug]: Text after an inline <link>, <meta>, <style> or <noscript> is dropped from cleaned_html and markdownPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto🐞 Bug 🩺 Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
unclecode/crawl4ai#2416 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
[Bug]: Since 0.9.1, Linux launches chrome-headless-shell instead of ChromiumPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto🐞 Bug 🩺 Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
unclecode/crawl4ai#2382 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
[Bug]: BestFirstCrawlingStrategy fetches a full batch past max_pagesPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto🐞 Bug 🩺 Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
unclecode/crawl4ai#2381 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
[Bug]: CrawlerRunConfig.clone() drops fallback_fetch_function, so deep crawls never use itPosiblemente ocupada @siva23367 la tomó hace 2 días. Abierto🐞 Bug 🩺 Needs Triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
unclecode/crawl4ai#2379 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
[Bug]: Docker memory guard reads host RAM when no container limit is set — cgroup v2 "max" defeats get_container_memory_percentPosiblemente ocupada @Battleplus la tomó hace 58 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
unclecode/crawl4ai#2123 · 1 comentario ·
Los mantenedores suelen responder en 3 días
Todos los issues de unclecode/crawl4ai
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
pgvector/pgvector-python#169 ·
-
ChaosEngine: backup-remote doctor row passes while consumer mode hides bot memory from gitPosiblemente ocupada @MohabMohie la tomó hoy. Abiertoagentic
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
ShaftHQ/SHAFT_ENGINE#7263 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 87/100
huggingface/smolagents#3045 ·
Los mantenedores suelen responder en 1 día