OpenAPI 3.2 SSE schemas misdescribe unnamed events and literal payloads
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 55/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- json, openapi
- Ambito
- backend-api-design
Direzione di ricerca
The bug lives in the TypeSpec OpenAPI 3.2 emitter's SSE stream schema generation. Start by finding the code that produces itemSchema for SSEStream<T> unions, focusing on the paths left unchanged by PR 12021. Add regression tests in the compiler/emitter test harness for unnamed events, text/JSON sentinels, JSON terminal literals, and missing data. Validate output against OpenAPI 3.2 SSE rules and JSON Schema Draft 2020-12.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Problem
OpenAPI 3.2 SSE emission still misdescribes valid streams with unnamed events, literal sentinels, and JSON-encoded terminal literals. These gaps are separate from the named model-valued terminal-event bug in https://github.com/microsoft/typespec/issues/11747.
https://github.com/microsoft/typespec/pull/12021 fixes that named-terminal case and preserves event names for named literal variants. The problems below are in code paths that the PR leaves unchanged.
This affects common contracts such as OpenAI Chat Completions (unnamed JSON chunks followed by a plain-text [DONE] sentinel) and Gemini streamGenerateContent (unnamed JSON records until EOF).
Reproduction
import "@typespec/http";
import "@typespec/events";
import "@typespec/sse";
using TypeSpec.Http;
using TypeSpec.Events;
using TypeSpec.SSE;
model Chunk {
text: string;
}
@events
union UnnamedEvents {
Chunk,
}
@route("/unnamed")
@get op unnamed(): SSEStream<UnnamedEvents>;
@events
union ChatEvents {
Chunk,
@contentType("text/plain")
@terminalEvent
"[DONE]",
}
@route("/chat")
@get op chat(): SSEStream<ChatEvents>;
@events
union JsonTerminalEvents {
@contentType("application/json")
@terminalEvent
"done",
}
@route("/json-terminal")
@get op jsonTerminal(): SSEStream<JsonTerminalEvents>;
@events
union NamedEvents {
chunk: Chunk,
}
@route("/named")
@get op named(): SSEStream<NamedEvents>;
emit:
- "@typespec/openapi3"
options:
"@typespec/openapi3":
openapi-versions: ["3.2.0"]
1. event is required even for unnamed events
All four emitted itemSchema objects use required: ["event"]. But this is a valid unnamed SSE event:
data: {"text":"hello"}
Its parsed field object, {"data":"{\"text\":\"hello\"}"}, fails validation with must have required property 'event'. An omitted or empty SSE event name defaults to message; the wire field need not exist.
2. Unnamed JSON and sentinel branches overlap
The /chat schema contains:
oneOf:
- properties:
data:
contentMediaType: application/json
contentSchema:
$ref: '#/components/schemas/Chunk'
- properties:
data:
contentMediaType: text/plain
const: '[DONE]'
Even if a parser materializes the default event name, {"event":"message","data":"[DONE]"} fails oneOf: both branches match.
contentMediaType and contentSchema are annotations, not default validation assertions. They do not distinguish the first branch from the sentinel branch. Multiple unnamed model variants have the same problem: different contentSchema annotations alone cannot make their outer oneOf branches exclusive.
3. JSON terminal literals constrain the wrong string
For /json-terminal, the emitter produces:
data:
contentMediaType: application/json
const: done
But the explicitly JSON-encoded literal "done" has this wire representation:
data: "done"
The parsed SSE data string includes the JSON quotation marks. With an explicit/default-materialized event: message, that valid JSON data fails the emitted const, whereas unquoted data: done passes.
Terminality should not determine whether a literal is treated as raw text or a JSON-encoded value. The declared payload content type should determine the representation.
4. data is not required
For /named, {"event":"chunk"} passes the emitted schema despite having no data. SSE does not dispatch a message when there are no data lines. OpenAPI 3.2's generic SSE event schema requires data, not event.
Expected
- Require
datafor dispatched SSE messages. - Require an event name on branches describing named events, not globally when unnamed/default-message variants are present.
- Preserve the distinction between named events and unnamed/default-message events in mixed unions.
- Avoid outer
oneOfbranches whose only differences are non-validating content annotations. For example, an assertion such asnot: { const: "[DONE]" }can exclude a plain-text sentinel from the JSON branch; other correct representations are possible. - Describe JSON literals inside
contentSchema, or constrain their correctly serialized wire string. Do not apply a raw-textconstsolely because the variant is terminal. - Keep event identity and payload representation unchanged when
@terminalEventis added. Representing the disconnect instruction itself is a separate extension/design question.
Useful regression cases include data-only JSON streams, data-only JSON plus a text sentinel, mixed named/default-message events, named text/plain terminal literals, unnamed JSON terminal literals, multiple unnamed model variants, and records missing data.
Verification and references
Reproduced with the existing compiler/emitter test harness and Ajv's Draft 2020-12 validator with default content-keyword behavior. The observations above do not depend on the named model-terminal branch changed in PR 12021.
- Lingua principale
- Java
- Stelle
- 5.9k
- Fork
- 421
- Merge medio
- 2g 3h
- PR unite (30g)
- 176
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/typespec
-
bug emitter:client:csharp
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
microsoft/typespec#12572 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Make website build cross-platform compatibleForse già presa @pcbeingused333 l’ha presa oggi. Apertabug meta:website
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
microsoft/typespec#12550 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Emitter framework: make the language-specific Alloy peer dependencies optionalForse già presa @timotheeguerin l’ha presa 8 giorni fa. Apertaemitter-framework feature triaged:core
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 62/100
microsoft/typespec#12476 · 1 commento · 2 reazioni · 2 assegnatari ·
I maintainer di solito rispondono entro 1 giorno
-
Upgrade C# generator Roslyn to 5.9.0Forse già presa @ArthurMa1978 l’ha presa 7 giorni fa. Apertaemitter:client:csharp feature
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
microsoft/typespec#12445 · 2 commenti · 1 reazione · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
bug emitter:client:python
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
microsoft/typespec#12360 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di microsoft/typespec
Issue simili
-
GCC 15.2: -Wcast-qual warning - cast discards const qualifier from pointer target type (H5trace.c)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] RemotingHelper.connect swaps the socket send and receive buffer configurationForse già presa @amalsaidov l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
needs-area-label
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 2 giorni
-
area/ai area/backend component/zeebe kind/task
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
spAnser/reacharound#38 ·