LibreOffice conversion Lambda wired via EventBridge; infinite-loop guard required for PDF passthrough

dropthejase built a separate x86_64 Lambda container running LibreOffice for DOCX-to-PDF conversion, triggered by EventBridge on docs-bucket uploads. The most operationally important piece: the Lambda must guard against re-triggering itself when it writes the converted PDF back to S3.

infrastructureworkflow

The conversion/ project gets its own TypeScript workspace with a production Dockerfile. The image uses Amazon Linux 2023 with dnf install -y libreoffice, npm ci --omit=dev, and node dist/index.js as the entry point. The architecture is x86_64 because LibreOffice is not available for ARM64 in the Lambda container base image.

The handler receives EventBridge S3 "Object Created" events - not S3Event Records arrays. The event shape difference matters: the key lives at event.detail.object.key, not event.Records[0].s3.object.key. 0ee216d fixes this after discovering the mismatch. Keys are URL-decoded with +-to-space replacement.

For .docx and .doc files the handler downloads from S3, runs docxToPdf() via the libreoffice-convert npm wrapper, uploads the PDF to converted-pdfs/ prefix, and updates document_versions.pdf_storage_path in Aurora via RDS Data API. For .pdf uploads, the file is already a PDF - the handler just records the source key as the pdf_storage_path.

The EventBridge infinite-loop fix (8afab46) is the critical operational piece. Three separate EventBridge rules (.docx, .doc, .pdf) are replaced by a single rule with a key prefix filter of documents/ and a suffix filter excluding the converted-pdfs/ prefix. Without this guard, the Lambda writing a converted PDF to the documents/ bucket would re-trigger itself.

libreoffice-convert is also removed from the API Lambda (1a11621) once the dedicated conversion Lambda takes ownership - the API Lambda had been carrying that dependency unnecessarily.

So what The EventBridge loop guard is a required step, not an afterthought - treat `8afab46` as mandatory reading before deploying any S3-triggered conversion pattern. The rest is a straightforward LibreOffice-in-Lambda wiring; the code is mostly reusable, though the Aurora Data API calls are fork-specific.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

8 commits from dropthejase/louis, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
510081c6 feat(conversion): scaffold project and copy conversion logic from backend Jason Lee 2026-05-06 ↗ GitHub
d42ae59b feat(conversion): add Lambda handler and production Dockerfile Jason Lee 2026-05-06 ↗ GitHub
90c65e48 Migrate conversion Lambda from Supabase to Aurora RDS Data API Jason Lee 2026-05-08 ↗ GitHub
43e9d4dd feat(infra): enable EventBridge on docs bucket, remove @supabase/supabase-js Jason Lee 2026-05-08 ↗ GitHub
b3674db6 infra: fix IAM permissions, EventBridge PDF rule, deploy-agent authorizer Jason Lee 2026-05-11 ↗ GitHub
commit body
- Lambda + AgentCore roles: extend Bedrock resources to wildcard region for
  cross-region inference profiles; add inference-profile/* ARN
- API Lambda role: upgrade sessionsBucket from grantRead to grantReadWrite
  (delete session on chat delete, Phase 5)
- AgentCore role: add s3:ListBucket on sessionsBucket
- AuthStack Identity Pool: add converted-pdfs/ prefix to per-user S3 policy
  and extend ListBucket condition to cover all three prefixes
- ConversionStack: add PDF EventBridge rule (documents/*.pdf trigger)
- deploy-agent.sh: include authorizer-configuration on update-agent-runtime
  (was only set on create); inject TABULAR_AGENT_ARN / MAIN_AGENT_ARN from
  SSM into runtime env vars (best-effort, skipped if not yet deployed)
0ee216da fix(conversion,chat): EventBridge format, PDF passthrough, delete session cleanup Jason Lee 2026-05-11 ↗ GitHub
1a11621d fix: remove libreoffice-convert from backend Lambda (conversion Lambda handles it) Jason Lee 2026-05-11 ↗ GitHub
8afab465 fix(conversion): stop EventBridge infinite loop on PDF uploads Jason Lee 2026-05-13 ↗ GitHub
commit body
Replace 3 separate EventBridge rules (docx/doc/pdf) with single rule
scoped to documents/ prefix only. Previous rules used OR semantics on
prefix+suffix - converted-pdfs/foo.pdf matched the .pdf suffix rule,
causing Lambda to re-trigger on every PDF it wrote. Added guard in
handler to reject keys outside documents/ as defense in depth.
Also upgrades base image to node22 (26.2-node22-x86_64).

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-331.md from inside the repo you want the changes in.

⬇ Download capture-thread-331.md