{"record":{"id":"f8d68d915b9486a0","repo":"siyuan-note/siyuan","slug":"proxy-output-requires-a-protocol-declaration-s","errorCode":null,"errorMessage":"proxy output requires a protocol declaration: %s","messagePattern":"proxy output requires a protocol declaration: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/proxy_protocol.go","lineNumber":91,"sourceCode":"\t\t\tpanic(\"invalid upstream proxy status\")\n\t\t}\n\t\treturn response.httpStatus\n\t}\n\tif response.directJSON {\n\t\tif response.httpStatus != 400 && response.httpStatus != 502 {\n\t\t\tpanic(\"undeclared proxy rejection status\")\n\t\t}\n\t\treturn response.httpStatus\n\t}\n\tif response.code == 0 {\n\t\tpanic(\"proxy response requires a stream or rejection\")\n\t}\n\treturn 200\n}\n\nfunc validateProxyDefinition(definition Definition) error {\n\tif (definition.Output == ProxyOutput) != (definition.Proxy != nil) {\n\t\treturn fmt.Errorf(\"proxy output requires a protocol declaration: %s\", definition.Name)\n\t}\n\tif definition.Proxy == nil {\n\t\treturn nil\n\t}\n\tif definition.Proxy.Kind != HTTPProxy && definition.Proxy.Kind != EventSourceProxy && definition.Proxy.Kind != WebSocketProxy {\n\t\treturn fmt.Errorf(\"unsupported proxy protocol: %s\", definition.Proxy.Kind)\n\t}\n\tif definition.Data != reflect.TypeFor[ProxyFailure]() || definition.DataOnError || definition.ErrorStatus != 0 || definition.SSE != nil || definition.WebSocket != nil {\n\t\treturn fmt.Errorf(\"invalid proxy response options: %s\", definition.Name)\n\t}\n\twant := ProxyOptions(definition.Proxy.Kind).Proxy\n\tif !reflect.DeepEqual(want, definition.Proxy) {\n\t\treturn fmt.Errorf(\"invalid proxy protocol declaration: %s\", definition.Name)\n\t}\n\treturn nil\n}\n\n// validateProxyHTTPResponse 按媒体类型区分上游字节和内核准入失败，同状态不代表同协议。","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/proxy_protocol.go#L73-L109","documentation":"validateProxyDefinition enforces that proxy configuration is all-or-nothing: a Definition must either declare both ProxyOutput and a Proxy protocol, or neither. If one side is set without the other, it returns \"proxy output requires a protocol declaration: %s\" naming the endpoint. This catches definitions that claim proxy output but lack the HTTP/EventSource/WebSocket proxy spec (or vice versa), which would leave the runtime unable to construct the upstream transport.","triggerScenarios":"Building a bundle (BuildBundle) with a Definition whose Output is ProxyOutput but whose Proxy field is nil; or conversely setting Definition.Proxy while leaving Output as a regular JSON output — surfaced by TestProxyDefinitionRejectsUnknownProtocol-style tests and at bundle construction time.","commonSituations":"A developer adds Output: ProxyOutput but forgets to fill Proxy: &Proxy{Kind: ...}; a refactor switches an endpoint to proxy mode and removes Output but forgets to remove Proxy; copy-pasted definitions partially updated; a code path builds Definitions programmatically and conditionally sets only one of the two fields.","solutions":["Set both fields consistently: Output: ProxyOutput together with a non-nil Proxy (HTTPProxy, EventSourceProxy, or WebSocketProxy)","If the endpoint is not a proxy, remove the Proxy declaration instead of leaving it set with a non-proxy Output","Or keep Proxy and add the missing Output: ProxyOutput to the definition","Add a unit test mirroring TestProxyDefinitionRejectsUnknownProtocol so BuildBundle fails fast with the endpoint name"],"exampleFix":"// before\nDefinition{Name: \"upstream\", Output: ProxyOutput}\n// after\nDefinition{Name: \"upstream\", Output: ProxyOutput, Proxy: &ProxyDeclaration{Kind: HTTPProxy}}","handlingStrategy":"validation","validationCode":"func proxyDeclarationIsConsistent(d Definition) bool { return (d.Output == ProxyOutput) == (d.Proxy != nil) }","typeGuard":null,"tryCatchPattern":"if err := BuildBundle(defs); err != nil { if strings.Contains(err.Error(), \"proxy output requires a protocol declaration\") { logDefectDefinition(err); return }; return err } — better: run validateProxyDefinition-equivalent checks in unit tests before BuildBundle","preventionTips":["Always set Output and Proxy together when introducing a proxy endpoint","Remove both fields together when converting an endpoint away from proxying","Mirror the validator's checks in unit tests like TestProxyDefinitionRejectsUnknownProtocol","Review diff hibernation: partial copy-paste of Definition structs is the usual culprit"],"tags":["config","proxy","schema-validation","plugin-api"],"backgroundTag":"missing-required-config-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}