{"record":{"id":"0b8e09338acdbd74","repo":"siyuan-note/siyuan","slug":"image-generation-was-cancelled","errorCode":null,"errorMessage":"image generation was cancelled","messagePattern":"image generation was cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/assets.go","lineNumber":440,"sourceCode":"\tif prompt == \"\" {\n\t\treturn GenerateImageResult{}, errors.New(\"prompt is required for image generation\")\n\t}\n\tsize := multimodalValueOrDefault(request.Size, Conf.AI.ImageGeneration.Size)\n\tquality := multimodalValueOrDefault(request.Quality, Conf.AI.ImageGeneration.Quality)\n\toutputFormat := strings.ToLower(multimodalValueOrDefault(request.OutputFormat, Conf.AI.ImageGeneration.OutputFormat))\n\tif outputFormat != \"png\" && outputFormat != \"jpeg\" && outputFormat != \"webp\" {\n\t\treturn GenerateImageResult{}, errors.New(\"unsupported image output format\")\n\t}\n\tgenerated, err := util.NewOpenAIImageAdapter(\n\t\tprovider.APIKey, provider.BaseURL, generationModel.Name, Conf.AI.ImageGeneration.RequestTimeout,\n\t).Generate(ctx, util.GenerateImageRequest{\n\t\tPrompt: prompt, Size: size, Quality: quality, OutputFormat: outputFormat,\n\t})\n\tif err != nil {\n\t\treturn GenerateImageResult{}, markImageExecutionUnknown(fmt.Errorf(\"generate image failed: %w\", err))\n\t}\n\tif ctx.Err() != nil {\n\t\treturn GenerateImageResult{}, markImageExecutionUnknown(errors.New(\"image generation was cancelled\"))\n\t}\n\treturn GenerateImageResult{\n\t\tData: generated.Data, MIMEType: generated.MIMEType, Extension: generated.Extension, RevisedPrompt: generated.RevisedPrompt,\n\t}, nil\n}\n\n// GenerateDocumentImage 使用通用图片生成能力创建文档资源。\nfunc GenerateDocumentImage(ctx context.Context, request GenerateDocumentImageRequest) (GenerateDocumentImageResult, error) {\n\tbt, err := resolveMultimodalDocument(request.DocumentID)\n\tif err != nil {\n\t\treturn GenerateDocumentImageResult{}, err\n\t}\n\tgenerated, err := GenerateImage(ctx, GenerateImageRequest{\n\t\tPrompt: request.Prompt, Size: request.Size, Quality: request.Quality, OutputFormat: request.OutputFormat,\n\t})\n\tif err != nil {\n\t\treturn GenerateDocumentImageResult{}, err\n\t}","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/assets.go#L422-L458","documentation":"Returned by GenerateImage when Generate returned no error but ctx.Err() is non-nil afterwards — the caller's context was cancelled or expired while the provider was processing. Because the request already reached the provider (and may still produce a charge/result), it is wrapped with markImageExecutionUnknown and must not be retried automatically.","triggerScenarios":"The user cancelled the operation; a parent context deadline (RequestTimeout or higher-level timeout) fired mid-call; the kernel is shutting down and cancelled the ctx.","commonSituations":"User clicked cancel on a long generation; the client closed the WebSocket/HTTP connection so the kernel cancelled the ctx; an aggressive outer timeout wraps the call.","solutions":["Treat as terminal for this attempt: do not retry on the same context (it is already cancelled).","If the user wants to try again, start a fresh context with a longer timeout and confirm with the user first.","Raise Conf.AI.ImageGeneration.RequestTimeout if cancellations are timeout-driven, not user-driven."],"exampleFix":"// before\nres, err := model.GenerateImage(ctx, req)\nif err != nil { res, err = model.GenerateImage(ctx, req) } // ctx already dead — loops forever\n\n// after\nres, err := model.GenerateImage(ctx, req)\nif err != nil && model.IsImageExecutionUnknown(err) {\n    if ctx.Err() != nil {\n        return fmt.Errorf(\"cancelled by caller (%w); not retrying\", ctx.Err())\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isCancelledGeneration(err error) bool {\n    return model.IsImageExecutionUnknown(err)\n}","tryCatchPattern":"res, err := model.GenerateImage(ctx, req)\nif err != nil && model.IsImageExecutionUnknown(err) && ctx.Err() != nil {\n    // caller's context died — do not retry on the same (dead) context\n    return fmt.Errorf(\"generation cancelled: %w\", ctx.Err())\n}","preventionTips":["Use a context with a timeout sized to real provider latency to avoid mid-call expiry.","On user cancel, treat the result as terminal — confirm before any retry.","Distinguish timeout-driven cancellation from user-driven cancellation in user messaging."],"tags":["context","ai","cancellation","no-retry"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}