Patch files reference
Reference for the patch files feature.
How it works
During generation, Speakeasy:
- Renders templates and produces the in-memory generated files.
- Looks for a matching patch file under
.speakeasy/patches/for each generated file. - Tries to apply the patch against the in-memory generated file using a Git-compatible diff parser/applier built into Speakeasy — no
gitbinary on the PATH is required at generation time. The outcome falls into one of three branches:- Clean apply. The patch applies without conflict. The patched content replaces the in-memory virtual file and proceeds to the disk-write step.
- Conflict. The patch’s context lines no longer match the regenerated content (typically because the upstream OpenAPI spec or templates changed in the patched region). Speakeasy rebases the patch onto the fresh output, writes any unresolved hunks as Git-style conflict markers (
<<<<<<<,=======,>>>>>>>) into the affected file, and fails the generation step with the list of conflicted files. The conflict markers are intentional — they invite a human to resolve the divergence and regenerate the patch from the resolved file. - Hard error. Any other apply failure (malformed patch syntax, unsupported diff operation such as create/delete/rename, filesystem read error other than
ENOENT) is surfaced directly. The generation step fails without rebase and without writing conflict markers. Errors across multiple patches are aggregated so a single run reports every malformed patch at once.
- Writes the result to disk. With custom code disabled, the patched content is written immediately. With custom code enabled, the write is deferred so the persistent-edits subsystem can reconcile patched content against on-disk manual edits first — see Interaction with custom code.
Location
All patch files live under .speakeasy/patches/ at the SDK output root. Subdirectories mirror the layout of the generated SDK:
my-sdk/
├── .speakeasy/
│ ├── gen.yaml
│ └── patches/
│ ├── src/
│ │ └── models/
│ │ └── payment.ts.patch ← patches src/models/payment.ts
│ └── package.json.patch ← patches package.json
├── src/
│ └── models/
│ └── payment.ts
└── package.jsonThe presence of any .patch file under .speakeasy/patches/ enables the feature for that generation — there is no change needed in gen.yaml.
File format
Each patch file is a unified diff in git diff format:
diff --git a/<path> b/<path>
--- a/<path>
+++ b/<path>
@@ -<old_start>,<old_count> +<new_start>,<new_count> @@
<context>
-<removed>
+<added>
<context>Speakeasy parses patches with Git-compatible semantics — quoted paths, multi-file diffs, and other valid git diff headers are accepted — but any tool that emits a unified diff (git diff, diff -u, IDE export) can author one. At generation time Speakeasy applies the patches in-process, so no git binary on the PATH is required.
Lookup rules
For each generated file Speakeasy looks for .speakeasy/patches/<relative_path>.patch:
- Path separators are normalized to
/. .and..segments are resolved (e.g.a/./b→a/b,a/b/../c→a/c).- The lookup is exact:
src/models/foo.tsis patched only by.speakeasy/patches/src/models/foo.ts.patch.
A patch whose filename does not match any generated file is still attempted as a “generic” multi-file patch against the in-memory generated file tree, using the diff’s +++ headers. Generic patches can only modify files that exist in the current generation’s virtual file set — disk-only files (manually authored, .genignored, or carried over from prior runs) cannot be patched.
Stale-patch behavior splits along path type:
- Exact-path orphan (e.g.
.speakeasy/patches/foo.go.patchwhenfoo.gois no longer generated): skipped, warning logged, generation continues. - Generic patch with missing targets:
ApplyPatchseterrors (cannot modify missing file) and generation fails.
Current limitations
Patch files currently modify the contents of existing generated files. The following diff operations are rejected with an error today:
- Creating new files.
- Deleting files.
- Renaming or moving files.
For workflows that need to add, remove, or move files, use custom code.
Error behavior
| Error | Behavior |
|---|---|
| Conflicts with regenerated content | Rebased onto fresh output; conflict markers; step fails |
| Malformed | Errors aggregated across all patches; step fails |
| Create / delete / rename | Rejected with error |
| Exact-path orphan (lookup file no longer generated) | Warning logged; generation continues |
| Generic patch targeting a file not in current generation | cannot modify missing file; step fails |
Filesystem read error (non-ENOENT) | Surfaced as error |
Interaction with custom code
Patch files and custom code (persistentEdits) are independent and can be used together. Speakeasy applies patch files after the regular generation pass and before the persistent-edits merge, so patched content participates in the 3-way merge for files that also have manual edits on disk.
Last updated on