Issues — issue_type Write/Read Asymmetry
The issue API is asymmetric for the issue type field: writes take a flat
issue_type_id scalar, while reads return a nested issue_type object.
Summary
| Direction | Field shape | Example |
|---|---|---|
| Write (POST /issues, PATCH /issues/{n}) | flat scalar issue_type_id (UUID string) |
{"issue_type_id":"<uuid>"} |
| Read (GET /issues, GET /issues/{n}, list) | nested issue_type object |
{"issue_type":{"id":"<uuid>","name":"Bug","slug":"bug",...}} |
A request that sends the read shape (issue_type — nested object, or a bare
string slug) on a write is rejected with HTTP 400 and the exact error
message:
issue_type is read-only; use issue_type_id
This is true for both POST (create) and PATCH (update).
Why the asymmetry exists
Reads return the fully-hydrated issue_type object so a client can render the
type's name, colour, and slug without a second lookup. Writes take only the
canonical issue_type_id UUID because the type catalogue is server-owned —
clients select an existing type by id; they do not define type attributes
inline. Accepting a nested issue_type object on write would be ambiguous (is
{"issue_type":{"name":"Bug"}} a lookup, an upsert, or a rename?), so the API
rejects it rather than silently dropping the field.
How the rejection is enforced
The issue create and update endpoints reject unknown fields. The request body is
parsed with strict decoding, so any field that is not part of the write contract
fails with HTTP 400. The write contract declares issue_type_id and not
issue_type, so a payload that carries an issue_type key is rejected.
For the issue_type key specifically, the 400 body's top-level error is the
canonical read-only message, and detail names the correct field:
{
"error": "issue_type is read-only; use issue_type_id",
"detail": "use \"issue_type_id\":\"<uuid>\" (canonical) — bare string slugs and nested {id} objects are not accepted"
}
Any other unknown field returns the generic unknown-field error in error with
a hint in detail. The top-level error field is the one a client should
branch on.
Client guidance
- To set or change an issue's type, send
"issue_type_id":"<uuid>"on POST/api/v1/repos/{owner}/{repo}/issues(issue_type_idis required when creating an issue) or PATCH/api/v1/repos/{owner}/{repo}/issues/{number}. - Do not echo a read response's nested
issue_typeobject back on a write — extract.issue_type.idintoissue_type_id. - Bare string slugs (
"issue_type":"bug") are also rejected — resolve the slug to a type UUID first.