{"record":{"id":"2ccc3c58b6567b42","repo":"sipeed/picoclaw","slug":"text-is-required","errorCode":null,"errorMessage":"text is required","messagePattern":"text is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/audio/tts/tts.go","lineNumber":108,"sourceCode":"\tctx context.Context,\n\tprovider TTSProvider,\n\tstore media.MediaStore,\n\ttext string,\n\tfilename string,\n\tchannel string,\n\tchatID string,\n) (string, error) {\n\tif provider == nil {\n\t\treturn \"\", fmt.Errorf(\"tts provider is not configured\")\n\t}\n\tif store == nil {\n\t\treturn \"\", fmt.Errorf(\"media store not configured\")\n\t}\n\tif channel == \"\" || chatID == \"\" {\n\t\treturn \"\", fmt.Errorf(\"no target channel/chat available\")\n\t}\n\tif strings.TrimSpace(text) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"text is required\")\n\t}\n\n\tstream, err := provider.Synthesize(ctx, text)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"tts synthesize failed: %w\", err)\n\t}\n\tdefer stream.Close()\n\n\terr = os.MkdirAll(media.TempDir(), 0o700)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create media temp dir: %w\", err)\n\t}\n\n\tfileExt := \".ogg\"\n\tcontentType := \"audio/ogg\"\n\tif provider.Name() == \"mimo-tts\" {\n\t\tfileExt = \".mp3\"\n\t\tcontentType = \"audio/mpeg\"","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/tts.go#L90-L126","documentation":"Returned by tts.SynthesizeAndStore when text is empty after strings.TrimSpace. Synthesis of blank input is meaningless, so the provider call is skipped. Typically the upstream LLM produced an empty or whitespace-only reply that was passed through unfiltered.","triggerScenarios":"The model's answer is '' or only spaces/newlines (e.g. it replied entirely via a tool call or image); the user typed only whitespace; the text was trimmed/consumed earlier by mistake.","commonSituations":"Voice-reply pipelines piping raw model output into TTS; edge-case prompts that yield empty completions.","solutions":["Filter empty/whitespace text before calling SynthesizeAndStore","When the model may answer non-verbally, fall back to a default phrase or skip voice output","Trim input early and log when synthesis is skipped"],"exampleFix":"// before\nref, err := tts.SynthesizeAndStore(ctx, p, s, reply, name, ch, id)\n// after\nif strings.TrimSpace(reply) == `` {\n    return ``, nil // nothing to say\n}\nref, err := tts.SynthesizeAndStore(ctx, p, s, reply, name, ch, id)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(text) == `` {\n    return nil // nothing to synthesize; skip gracefully\n}","typeGuard":"func isTextRequired(err error) bool {\n    return err != nil && err.Error() == `text is required`\n}","tryCatchPattern":"if err != nil && isTextRequired(err) {\n    // upstream produced no speakable text: skip voice output, optionally log\n}","preventionTips":["Trim and check model replies before entering the TTS pipeline","Handle non-verbal model answers (tool calls, images) with a default phrase or silence","Never pass raw unvalidated LLM output to SynthesizeAndStore"],"tags":["validation","tts","input"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}