{"record":{"id":"102e11eca49747b3","repo":"Tencent/WeKnora","slug":"extraction-model-returned-no-usable-output-within","errorCode":null,"errorMessage":"extraction model returned no usable output within %d tokens; if this is a reasoning model, its thinking is consuming the budget","messagePattern":"extraction model returned no usable output within (.+?) tokens; if this is a reasoning model, its thinking is consuming the budget","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/memory/extract.go","lineNumber":983,"sourceCode":"\t}\n\n\t// A truncated call is retried once with room to spare. Reasoning models\n\t// that ignore the disable flag spend the whole budget thinking and return\n\t// an empty string, which is indistinguishable from \"nothing to record\"\n\t// unless the finish reason is checked.\n\tif isTruncated(response) {\n\t\tlogger.Warnf(ctx,\n\t\t\t\"memory: extraction hit the token ceiling with %d chars of content, retrying with %d tokens\",\n\t\t\tlen(strings.TrimSpace(response.Content)), extractBudgetRetryTokens)\n\t\tresponse, err = s.completeExtraction(ctx, chatModel, userPrompt, extractBudgetRetryTokens)\n\t\tif err != nil {\n\t\t\treturn extractionResponse{}, err\n\t\t}\n\t\tif response == nil || isTruncated(response) {\n\t\t\t// Returning an error is what keeps the watermark where it is, so\n\t\t\t// these messages are read again rather than silently consumed by a\n\t\t\t// run that learned nothing.\n\t\t\treturn extractionResponse{}, fmt.Errorf(\n\t\t\t\t\"extraction model returned no usable output within %d tokens; \"+\n\t\t\t\t\t\"if this is a reasoning model, its thinking is consuming the budget\",\n\t\t\t\textractBudgetRetryTokens)\n\t\t}\n\t}\n\n\tparsed, err := parseExtractionResponse(response.Content)\n\tif err != nil {\n\t\t// A malformed but complete response is the model's fault, not a\n\t\t// transient failure: the same prompt at temperature zero produces the\n\t\t// same garbage, so retrying only burns the budget. Truncation is\n\t\t// handled above precisely because it is *not* this case.\n\t\tlogger.Warnf(ctx, \"memory: unparsable extraction response: %v\", err)\n\t\treturn extractionResponse{}, nil\n\t}\n\treturn parsed, nil\n}\n","sourceCodeStart":965,"sourceCodeEnd":1001,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/memory/extract.go#L965-L1001","documentation":"The extraction model call completed but produced nil output or output flagged as truncated within the retry token budget, so no usable extraction result exists. Commonly a reasoning model whose thinking tokens exhaust the budget before emitting the final answer. Returning the error keeps the watermark in place so messages are retried.","triggerScenarios":"callExtractionModel invoked with a small extractBudgetRetryTokens budget against a model that returns nil or a truncated response (finish_reason=length).","commonSituations":"Switching the memory extraction model to a reasoning model (o1/deepseek-r1 style) whose hidden thinking consumes the token budget; budget configured too low for long message batches; provider returning truncated responses under load.","solutions":["Switch the workspace memory extraction model to a non-reasoning chat model","Increase the extraction token budget in workspace memory settings","Shorten the input message batch so the output fits the budget","Check the wrapped response's finish_reason to confirm truncation before tuning"],"exampleFix":"// before\n// reasoning model with default budget\n\"memory.extractionModel\": \"deepseek-r1\"\n// after\n\"memory.extractionModel\": \"claude-3-5-sonnet\" // non-reasoning model that emits output within budget","handlingStrategy":"fallback","validationCode":"// prefer non-reasoning models for extraction, or raise the budget\nif isReasoningModel(modelID) {\n\tbudget = reasoningModelBudget // larger token budget\n}","typeGuard":null,"tryCatchPattern":"_, err := memorySvc.Handle(ctx, run)\nif err != nil && strings.Contains(err.Error(), \"no usable output within\") {\n\t// switch extraction model or increase budget, then re-run;\n\t// the watermark is intact so messages will be re-read\n\treturn ErrExtractionBudgetExhausted\n}","preventionTips":["Use non-reasoning chat models for memory extraction","Size the extraction budget to the model family (reasoning models need far more)","Monitor finish_reason=length on extraction calls","Keep per-run message batches small enough to fit the output budget"],"tags":["memory","model","token-budget"],"backgroundTag":"model-output-truncated","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}