---
title: "Codegen Troubleshooting"
canonical: "https://test.abhinandan.one/codegen-troubleshooting"
markdown_url: "https://test.abhinandan.one/codegen-troubleshooting.md"
publisher: "Primitive SDKs"
kind: "troubleshooting"
content_type: "reference"
category: "Troubleshooting"
description: "Fixing codegen regeneration errors involves rerunning generate-openapi-artifacts.ts, fix-generated-api-imports.ts, or the Python generate_api_client.py fixup pass."
keywords: ["fix-generated-api-imports.ts", "MemoryJsonValue", "Unable to repair generated MemoryJsonValue type", "guardOptionalBodyContentType", "generate_api_client.py", "openapi-python-client"]
last_modified: "2026-08-11T18:55:08.915447+00:00"
published_at: "2026-08-11T18:55:08.7777+00:00"
source_files:
  - "packages/api-core/scripts/fix-generated-api-imports.ts"
  - "sdk-python/scripts/generate_api_client.py"
sections:
  - {anchor: "unable-to-repair-generated-memoryjsonvalue-type", title: "Unable to repair generated MemoryJsonValue type"}
  - {anchor: "generated-imports-missing-js-extensions", title: "Generated imports missing `.js` extensions"}
  - {anchor: "optional-body-request-sends-content-type-applicationjson-with-no-body", title: "Optional-body request sends `Content-Type: application/json` with no body"}
  - {anchor: "duplicate-imports-in-the-generated-python-client", title: "Duplicate imports in the generated Python client"}
  - {anchor: "file-payload-responses-wrap-responsetext-instead-of-responsecontent", title: "File payload responses wrap `response.text` instead of `response.content`"}
  - {anchor: "not-sure-which-fixup-is-out-of-sync", title: "Not sure which fixup is out of sync"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Codegen Troubleshooting

Diagnoses the three regeneration failures that break generated SDK clients: schema drift in MemoryJsonValue, stale imports missing.js extensions, and mismatched Content-Type headers on optional-body requests.

Both the Node client in [api-core](https://test.abhinandan.one/api-core-overview.md) and the Python client under `primitive.api` run a post-generation fixup script that repairs known generator output problems. When a generator version bump changes that output, the fixup either throws or stops matching, and this page lists the failures you'll see.

For the end-to-end regeneration command sequence, see [Regenerating SDK Code from the OpenAPI Spec](https://test.abhinandan.one/codegen-workflow.md). For how the pipeline fits together, see [Architecture: Shared Codegen Pipeline](https://test.abhinandan.one/codegen-architecture.md).

## Unable to repair generated MemoryJsonValue type

`fixMemoryJsonValueType` in `packages/api-core/scripts/fix-generated-api-imports.ts` throws this when its regex no longer matches the generated `MemoryJsonValue` alias:

```text
Unable to repair generated MemoryJsonValue type in <file>. The codegen output shape changed; update fixMemoryJsonValueType before publishing.
```

**Cause**: `@hey-api/openapi-ts` treats the OpenAPI 3.1 `type: "null"` branch of this recursive JSON schema as `unknown`, which widens the whole generated alias, so the fixup script patches the alias back to the exact JSON type (see [Generated TypeScript Client Fixups](https://test.abhinandan.one/typescript-client-fixups.md) for the full explanation of this repair). It matches the generated block with a regex replacement:

```typescript
// packages/api-core/scripts/fix-generated-api-imports.ts
const MEMORY_JSON_VALUE_PATTERN =
  /export type MemoryJsonValue = [\s\S]*?;\n\n\/\*\*\n \* Memory scope\./;
```

That pattern anchors on the literal text immediately following the generated type, including the `* Memory scope.` docstring on the next exported symbol. If the OpenAPI spec's schema ordering changes, or `@hey-api/openapi-ts` changes its comment or type-emission format, the pattern no longer matches and the script throws rather than silently leaving the `unknown`-widened type in place.

**Fix**: open the newly generated file (under `packages/api-core/src/api/`) and check what `export type MemoryJsonValue = ...` and the symbol that follows it actually look like now. Update `MEMORY_JSON_VALUE_PATTERN` and `MEMORY_JSON_VALUE_TYPE` in `fix-generated-api-imports.ts` to match the new shape, then re-run:

```bash
cd packages/api-core
pnpm generate:api
```

Confirm the fix by grepping the output for the exact type:

```bash
# from packages/api-core
grep -rA3 "export type MemoryJsonValue" src/api
```

You should see the null-inclusive union `string | number | boolean | Array<MemoryJsonValue> | { [key: string]: MemoryJsonValue } | null`, not `unknown`. See [Memory Value Validation Helper](https://test.abhinandan.one/memory-json-value-helper.md) for how `isMemoryJsonValue` depends on this type staying accurate.

## Generated imports missing `.js` extensions

Relative imports in the generated TypeScript need a `.js` extension for Node ESM resolution, and `@hey-api/openapi-ts` emits them without one, so unresolvable-module errors mean the fixup pass did not run.

**Cause**: the generator emits relative specifiers such as `from "./types"`, and `fix-generated-api-imports.ts` rewrites them to add `.js` (or `/index.js` for directory imports), as described in [Generated TypeScript Client Fixups](https://test.abhinandan.one/typescript-client-fixups.md):

```typescript
// packages/api-core/scripts/fix-generated-api-imports.ts
function addJsExtension(file: string, specifier: string): string {
  if (!specifier.startsWith("./") && !specifier.startsWith("../")) {
    return specifier;
  }
  if (specifier.endsWith(".js") || specifier.endsWith(".json")) {
    return specifier;
  }
  // ...resolves whether the target is a directory (adds /index.js)
  // or a file (adds .js)
}
```

**Fix**: this step runs automatically as part of `pnpm generate:api` (which calls `openapi-ts` then `tsx scripts/fix-generated-api-imports.ts`). If you see unfixed imports, you likely ran the raw `openapi-ts` generator directly and skipped the fixup script. Re-run the full sequence:

```bash
cd packages/api-core
pnpm generate:api
```

Never hand-edit import paths in generated files, the next regeneration overwrites them.

## Optional-body request sends `Content-Type: application/json` with no body

The generator adds the JSON `Content-Type` header to every operation that declares a request body, so optional-body operations send the header even when the caller omits the payload, which is wrong on the wire.

**Cause**: `@hey-api/openapi-ts` unconditionally emits:

```typescript
// generated operation, before the fixup
headers: { 'Content-Type': 'application/json', ...options.headers }
```

on every operation whose OpenAPI schema declares a request body, regardless of whether that body is *required*. The optional-body operations affected are `testFunction`, `cli_logout`, `start_cli_login`, and `search_emails`. `fix-generated-api-imports.ts` guards this with `guardOptionalBodyContentType`, which rewrites the header assignment to fire only when a body is actually present (see [Generated TypeScript Client Fixups](https://test.abhinandan.one/typescript-client-fixups.md)):

```typescript
// packages/api-core/scripts/fix-generated-api-imports.ts
function guardOptionalBodyContentType(content: string): string {
  return content.replace(
    /(headers:\s*\{\n\s*)'Content-Type':\s*'application\/json',\n(\s*\.\.\.options\.headers\n\s*\})/g,
    "$1...(options.body !== undefined && { 'Content-Type': 'application/json' }),\n$2",
  );
}
```

Required-body operations are unaffected: the type system guarantees `body` is always defined for those, so the header still ships in practice.

**Fix**: this transform runs automatically inside `fix-generated-api-imports.ts`. If a generated operation still emits the unconditional header:

1. Confirm you ran `pnpm generate:api` (not the raw `openapi-ts` command alone).
2. Check whether the operation's shape in the generated source still matches the regex, `@hey-api/openapi-ts` version bumps can reformat the emitted `headers: {...}` block (different whitespace, reordered keys) and silently break the match. Diff the generated file's `headers:` block against the pattern and update the regex in `guardOptionalBodyContentType` if the format changed.
3. Re-run generation and grep for the guarded form:

```bash
# from packages/api-core
grep -rA2 "'Content-Type'" src/api
```

You should see `...(options.body !== undefined && { 'Content-Type': 'application/json' })`, not a bare `'Content-Type': 'application/json',`.

The Python client has the analogous problem and fix in `sdk-python/scripts/generate_api_client.py`'s `guard_optional_body_content_type`, which re-indents the same unconditional header assignment so it only fires inside the `if not isinstance(body, Unset):` block. If you hit the Python equivalent, re-run:

```bash
cd sdk-python
uv run python scripts/generate_api_client.py
```

and confirm with:

```bash
# from sdk-python
grep -rB2 'headers\["Content-Type"\] = "application/json"' src/primitive/api/api
```

The line should be indented inside the `if not isinstance(body, Unset):` block, not at the function's top level.

## Duplicate imports in the generated Python client

`openapi-python-client` 0.28.3 occasionally emits the same import line twice, so strict linters such as ruff and basedpyright flag repeated `from ... import ...` lines in generated model files.

**Cause**: the duplication shows up most often as `from ..types import UNSET, Unset` in 201-response models. It is semantically a no-op but trips downstream strict linters.

**Fix**: `generate_api_client.py`'s `dedupe_imports` runs automatically after generation and strips repeated module-level import lines (it only matches unindented lines, so imports inside `TYPE_CHECKING` blocks or function bodies are left alone). If you still see duplicates after generating, confirm you ran the wrapper script rather than calling `openapi-python-client generate` directly:

```bash
cd sdk-python
uv run python scripts/generate_api_client.py
```

## File payload responses wrap `response.text` instead of `response.content`

Generated Python file-download responses wrap the response in `BytesIO`, which requires `bytes`, so `openapi-python-client`'s `BytesIO(response.text)` output has to be rewritten.

**Fix**: `use_bytes_for_file_responses` in `sdk-python/scripts/generate_api_client.py` rewrites `BytesIO(response.text)` to `BytesIO(response.content)` automatically, as part of the same `generate_api_client.py` invocation above. There is no separate command to run.

## Not sure which fixup is out of sync

Regenerate everything from the OpenAPI source of truth rather than patching generated files by hand:

```bash
make node-generate python-generate go-generate
```

> **Warning:** Never hand-edit files under `packages/api-core/src/api/` or `sdk-python/src/primitive/api/`. Any manual fix is silently overwritten the next time someone runs the generator. Fix the *script* that produces the file, not the file itself.

After regenerating, run each SDK's checks to catch anything the fixups missed:

```bash
make node-check python-check go-check
```
