{"record":{"id":"1878cad43b0f8fa0","repo":"siyuan-note/siyuan","slug":"invalid-request-s","errorCode":null,"errorMessage":"invalid request: %s","messagePattern":"invalid request: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/ai_input.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n)\n\nfunc aiStructDecoder[Request, Data any](endpoint *Endpoint[Request, Data], legacyName string) {\n\tendpoint.decodeRequest = func(reader io.Reader) (value Request, err error) {\n\t\terr = json.NewDecoder(reader).Decode(&value)\n\t\tif err != nil {\n\t\t\tmessage := strings.ReplaceAll(err.Error(), reflect.TypeFor[Request]().Name()+\".\", legacyName+\".\")\n\t\t\tfor _, name := range []string{\"Reference\", \"EditorContext\", \"FrontendCapability\", \"ToolEffects\"} {\n\t\t\t\tmessage = strings.ReplaceAll(message, \"AI\"+name+\".\", name+\".\")\n\t\t\t}\n\t\t\terr = fmt.Errorf(\"invalid request: %s\", message)\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc (r AIProviderRequest) ProviderError() error { return r.providerError }\n\nfunc aiLegacyString(fields fileTreeFields, key string, required, trim bool) (value string, err error) {\n\tvalue, err = legacyField[string](fields, key, \"String\", required)\n\tif err == nil && trim {\n\t\tvalue = strings.TrimSpace(value)\n\t\tif value == \"\" {\n\t\t\terr = fmt.Errorf(\"Field [%s] must not be empty\", key)\n\t\t}\n\t}\n\treturn\n}\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/ai_input.go#L2-L38","documentation":"AIProviderRequest's request binding decodes the incoming JSON body into a Request value; when json.Decode fails, the raw decoder error is rewritten (renaming internal struct names back to legacy field names) and wrapped as 'invalid request: %s'. It means the request body sent to the AI API does not match the expected schema.","triggerScenarios":"POSTing a body to the AI input endpoint that is not valid JSON or does not unmarshal into the Request struct: wrong field types, unknown malformed payload, trailing garbage, or truncated JSON.","commonSituations":"Client sends form-encoded data instead of JSON; a field typed as string receives a number; plugin uses an outdated request shape; body got truncated by a proxy.","solutions":["Inspect the wrapped message after 'invalid request: ' — it names the exact JSON decode problem","Validate the payload against the Request schema before sending","Send Content-Type: application/json with a syntactically valid JSON body","Update the plugin/client to the current AI request format"],"exampleFix":"// before\nfetch(url, {body: \"prompt=hi\"}) // form-encoded\n// after\nfetch(url, {\n  headers: {\"Content-Type\": \"application/json\"},\n  body: JSON.stringify({prompt: \"hi\"})\n})","handlingStrategy":"try-catch","validationCode":"try { JSON.parse(payload); } catch (e) { throw new Error(\"body is not valid JSON\"); }","typeGuard":null,"tryCatchPattern":"try {\n  await fetchPost(\"/api/ai/...\", payload);\n} catch (e) {\n  if (String(e).includes(\"invalid request:\")) {\n    console.error(\"Schema mismatch:\", e.message); // details follow the prefix\n  }\n}","preventionTips":["Always send Content-Type: application/json","JSON.stringify the payload; never hand-build JSON strings","Validate field types against the Request schema before sending","Keep plugins updated to the current AI request format"],"tags":["json","validation","api"],"backgroundTag":"json-decode-failed","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"}