Skip to content

Shank Errors and Pitfalls

Shank does not run at request time, so “errors” here are split into two buckets: build/export errors that surface during pnpm dev / pnpm export, and render errors that surface only once sirloin substitutes placeholders and a client renders the HTML.

Build / export errors

email dev fails to start

  • Port already in use. email dev defaults to 3000. Kill the other process or run pnpm dev -- --port 3001.
  • react/react-dom version drift. react-templates/package.json pins react@19.0.0 and react-dom@19.0.0 and depends on react-email 3.0.6 and @react-email/components 0.0.32. If a transitive bump pulls React 18, JSX from @react-email/components will throw at render time. Run pnpm install from apps/shank/react-templates/ (not from the monorepo root) to get the locally pinned versions.

pnpm export writes nothing or writes to the wrong place

  • The export path is hardcoded relative to apps/shank/react-templates/: --outDir='../../sirloin/internal/pkg/emails/templates'.
  • If you run email export from a different directory, the output lands in the wrong tree. Always run from apps/shank/react-templates/.
  • If the path is missing (e.g. fresh checkout that pruned the sirloin tree), the command silently fails to create directories. Verify with git status apps/sirloin/internal/pkg/emails/templates/.

Tailwind classes do not appear in exported HTML

  • react-email inlines styles at export time via the Tailwind wrapper component. If you import a component outside of <Tailwind>{...}</Tailwind>, classes are dropped. Both existing templates wrap their entire body in <Tailwind> — keep that pattern.

Render-time errors (after sirloin sends)

Placeholder appears literally in the email

Symptom: the recipient sees {{CHARACTER_NAME}} instead of the character’s name.

  • The token in the TSX must match the constant in apps/sirloin/internal/pkg/emails/client.go exactly. Sirloin uses strings.ReplaceAll(body, "{{CHARACTER_NAME}}", characterName) — a typo on either side leaves the literal token in the output.
  • Add new tokens in both places (template and Send* method) in the same change.

Missing prop / empty value

Sirloin passes raw strings to ReplaceAll. If a caller passes "", the placeholder is replaced with an empty string and the email renders with “Hey ! Your character is ready” (note the double space). Validate inputs in the caller, not in the template.

Wrong default export in training-failed.tsx

apps/shank/react-templates/emails/training-failed.tsx currently declares export const TrainingDoneEmail = ... and export default TrainingDoneEmail inside the file (verified at last review). This is a copy-paste artifact — the preview will mis-label the template, and any tooling that keys off the component name will report training-failed as TrainingDoneEmail. Rename to TrainingFailedEmail when next touched. The exported HTML (apps/sirloin/internal/pkg/emails/templates/training-failed.html) is not affected: the file has no <title> element and the <Preview> text is the correct Your character {{CHARACTER_NAME}} training failed string. The bug is purely cosmetic in the TSX preview and any tooling that keys off the exported component name.

PII leaks via preview text

Preview text (<Preview>...</Preview>) is interpolated with the same {{CHARACTER_NAME}} token. Some inboxes display preview text in notifications. Avoid putting anything more sensitive than a character name into preview strings. See docs/src/content/docs/standards/security-model.md for the canonical PII classes.

Email-client compatibility

  • Outlook (desktop) renders via Word. Avoid CSS not supported there: flexbox, grid, position: absolute, custom fonts. Stick to the components from @react-email/components — they ship Outlook-safe markup.
  • Dark-mode handling: react-email does not auto-invert images. Provide light-on-transparent assets where possible. Existing templates use https://images.foxy.ai/logo1_*.png which is light.
  • Litmus / Email-on-Acid: not currently wired into the build (no external visual-check tooling appears in apps/shank/react-templates/ or .github/workflows/). TODO(@zen): document whether designers run external visual checks before merging; if so, add the checklist link to shank-runbook.md.

When in doubt

Run pnpm dev and inspect the live preview before exporting. The preview matches the exported HTML 1:1 except for the unsubstituted {{TOKEN}} placeholders.