OpenAPI 3.2 SSE schemas misdescribe unnamed events and literal payloads
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 55/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- json, openapi
- Área
- backend-api-design
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Java
- Estrellas
- 5.9k
- Forks
- 421
- Merge medio
- 2 d 3 h
- PR fusionados (30 d)
- 176
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/typespec
-
bug emitter:client:csharp
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
microsoft/typespec#12572 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Make website build cross-platform compatiblePosiblemente ocupada @pcbeingused333 la tomó hoy. Abiertobug meta:website
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
microsoft/typespec#12550 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Emitter framework: make the language-specific Alloy peer dependencies optionalPosiblemente ocupada @timotheeguerin la tomó hace 8 días. Abiertoemitter-framework feature triaged:core
Dificultad 1/5 Menos de una hora Aptitud para principiantes 62/100
microsoft/typespec#12476 · 1 comentario · 2 reacciones · 2 asignados ·
Los mantenedores suelen responder en 1 día
-
Upgrade C# generator Roslyn to 5.9.0Posiblemente ocupada @ArthurMa1978 la tomó hace 7 días. Abiertoemitter:client:csharp feature
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
microsoft/typespec#12445 · 2 comentarios · 1 reacción · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
bug emitter:client:python
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
microsoft/typespec#12360 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
Todos los issues de microsoft/typespec
Issues similares
-
GCC 15.2: -Wcast-qual warning - cast discards const qualifier from pointer target type (H5trace.c)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 73/100
Los mantenedores suelen responder en 1 día
-
[Bug] RemotingHelper.connect swaps the socket send and receive buffer configurationPosiblemente ocupada @amalsaidov la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
needs-area-label
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 2 días
-
area/ai area/backend component/zeebe kind/task
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
spAnser/reacharound#38 ·