{"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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/ai.go","lineNumber":174,"sourceCode":"\tif !Conf.AI.HasAnyProvider() {\n\t\treturn nil, errors.New(\"no AI provider configured\")\n\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)\n\tcompletionStream, err := util.CreateOpenAICompletionStream(streamCtx, client, prov.Protocol, req, nil)\n\trequestTimedOut := stopAIEditorCancelTimer(requestTimer, requestTimerDone)","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/ai.go#L156-L192","documentation":"After optionally filling input from the given block IDs, the library builds the editor prompt and validates it. If the resulting prompt is entirely whitespace, there is nothing for the AI editor to act on, so the request is refused before contacting the model.","triggerScenarios":"Calling NewAIEditorChatStream with an empty input string and either no ids, ids resolving to empty content, or an action that produces no prompt text; BuildAIEditorPrompt output trims to \"\".","commonSituations":"Invoking AI editing on an empty document/block; passing whitespace-only input; action prompt that interpolates only empty user content; getBlocksContent returning nothing for stale/deleted block IDs.","solutions":["Pass non-empty input text or valid block IDs whose content exists","Check that the block IDs passed still exist (getBlocksContent returns content only for live blocks)","Verify the selected action's prompt template yields non-empty text for the given input"],"exampleFix":"// before\nstream, err := NewAIEditorChatStream(\"\", action, history, nil)\n// after\nif strings.TrimSpace(input) == \"\" {\n    return errors.New(\"provide text or valid block IDs\")\n}\nstream, err := NewAIEditorChatStream(input, action, history, ids)","handlingStrategy":"validation","validationCode":"if (!input.trim() && !(ids && ids.length)) { throw new Error('Provide input text or block IDs') }","typeGuard":null,"tryCatchPattern":"try { await startAIEditor() } catch (e) { if (e.message === 'AI editor input is empty') showToast('Select a block or enter text first') }","preventionTips":["Check the selection is non-empty before invoking AI editing","Validate block IDs still exist before passing them","Test action prompts with empty input to ensure they produce text"],"tags":["ai","validation","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}