{"record":{"id":"c71b96d294bb081e","repo":"siyuan-note/siyuan","slug":"ai-editor-input-is-empty","errorCode":null,"errorMessage":"AI editor input is empty","messagePattern":"AI editor input is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/ai.go","lineNumber":176,"sourceCode":"\t}\n\n\tprov, m := Conf.AI.GetEditingModel()\n\tif nil == prov || nil == m {\n\t\treturn nil, errors.New(\"no AI editing model configured\")\n\t}\n\tediting := Conf.AI.Editing\n\tif nil == editing {\n\t\treturn nil, errors.New(\"no AI editing config\")\n\t}\n\n\tif \"\" == input && 0 < len(ids) {\n\t\tinput = getBlocksContent(ids)\n\t}\n\tprompt := BuildAIEditorPrompt(input, action)\n\tif \"\" == strings.TrimSpace(prompt) {\n\t\treturn nil, errors.New(\"AI editor input is empty\")\n\t}\n\n\tmessages := buildAIEditorMessages(prompt, history, editing.MaxHistoryMessages)\n\n\treq := openai.ChatCompletionRequest{\n\t\tModel:               m.Name,\n\t\tMaxCompletionTokens: editing.MaxCompletionTokens,\n\t\tTemperature:         float32(editing.Temperature),\n\t\tMessages:            messages,\n\t\tStream:              true,\n\t}\n\tstreamCtx, cancel := context.WithCancel(ctx)\n\tstreamCtx = util.ContextWithOpenAIResponsesBaseURL(streamCtx, prov.BaseURL)\n\trequestTimeout := time.Duration(prov.RequestTimeout) * time.Second\n\trequestTimer, requestTimerDone := startAIEditorCancelTimer(requestTimeout, cancel)\n\tclient := util.NewOpenAIClientWithModel(prov.APIKey, prov.BaseURL, m.Name, ResolveAIProviderHeaders(prov))\n\tcompletionStream, err := util.CreateOpenAICompletionStream(streamCtx, client, prov.Protocol, req, nil)\n\trequestTimedOut := stopAIEditorCancelTimer(requestTimer, requestTimerDone)\n\tif requestTimedOut {\n\t\terr = errors.New(\"AI editor request timeout\")","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/ai.go#L158-L194","documentation":"After resolving provider, model, and editing config, NewAIEditorChatStream builds the prompt via BuildAIEditorPrompt(input, action). If the trimmed prompt is empty — because the caller passed an empty input, the referenced block IDs yielded no content, or the action template produced nothing — SiYuan refuses to send a useless request to the model and throws this error.","triggerScenarios":"Calling NewAIEditorChatStream (aiEditorChatContract) with input=\"\" and ids=[] (or ids pointing to blocks whose content is empty/unavailable); an action whose prompt template renders to whitespace-only output.","commonSituations":"Frontend invoking AI on an empty editor or an empty block; stale/invalid block IDs after the document was deleted or IDs were changed; a custom AI action whose template no longer matches the supplied input.","solutions":["Pass non-empty `input` text or valid `ids` of existing blocks with content","Verify the block IDs exist: fetch them first (e.g. via /api/filetree/getDoc) before calling the AI editor endpoint","Check the action/prompt template — fix or re-select an action that produces non-empty prompt text","Trim-guard on the client: refuse to invoke AI editing when the selection is empty"],"exampleFix":"// before\nNewAIEditorChatStream(ctx, nil, \"\", action, history)\n// after\nif strings.TrimSpace(input) == \"\" && len(ids) == 0 { return errors.New(\"nothing selected for AI editing\") }\nNewAIEditorChatStream(ctx, ids, input, action, history)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(input) === \"\" && (!ids || ids.length === 0) {\n  throw new Error(\"Provide input text or valid block IDs for AI editing\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fetchPost(\"/api/ai/editor/chatStream\", payload);\n} catch (e) {\n  if (e.message.includes(\"input is empty\")) showMsg(window.siyuan.languages.emptyContent);\n}","preventionTips":["Disable AI actions on empty selections in the UI","Verify block IDs exist before sending them","Test custom action templates with minimal input"],"tags":["ai","validation","empty-input"],"backgroundTag":"empty-required-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"}