{"record":{"id":"12f281d62fc01890","repo":"sipeed/picoclaw","slug":"failed-to-write-tts-audio-w","errorCode":null,"errorMessage":"failed to write tts audio: %w","messagePattern":"failed to write tts audio: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/tts/tts.go","lineNumber":150,"sourceCode":"\t\t}\n\t}\n\n\tfile, err := os.CreateTemp(media.TempDir(), \"tts-*\"+fileExt)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create temp file: %w\", err)\n\t}\n\n\tremoveTemp := true\n\tdefer func() {\n\t\tif removeTemp {\n\t\t\t_ = os.Remove(file.Name())\n\t\t}\n\t}()\n\n\t_, err = io.Copy(file, stream)\n\tif err != nil {\n\t\t_ = file.Close()\n\t\treturn \"\", fmt.Errorf(\"failed to write tts audio: %w\", err)\n\t}\n\n\terr = file.Close()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to close tts audio file: %w\", err)\n\t}\n\n\tfilename = strings.TrimSpace(filename)\n\tif filename == \"\" {\n\t\tfilename = fmt.Sprintf(\"tts-%d%s\", time.Now().Unix(), fileExt)\n\t}\n\n\text := strings.ToLower(filepath.Ext(filename))\n\tif ext == \"\" {\n\t\tfilename += fileExt\n\t} else if ext != fileExt {\n\t\tfilename = strings.TrimSuffix(filename, filepath.Ext(filename)) + fileExt\n\t}","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/tts.go#L132-L168","documentation":"io.Copy(file, stream) failed at pkg/audio/tts/tts.go:150 while copying the provider's synthesized audio (OpenAI-protocol or mimo TTS HTTP body) into the temp file. Failure is either upstream (connection dropped mid-body, provider closed early, proxy cut the stream) or local (disk write error). The temp file is closed and removed by the removeTemp defer, so no partial file leaks.","triggerScenarios":"Synthesizing long text where the provider or an intermediary proxy times out mid-body; provider rate-limiting that truncates the response; connection reset between Synthesize() returning and the copy finishing; ENOSPC while writing the audio bytes.","commonSituations":"Flaky egress NAT dropping idle-ish streaming connections; corporate proxy with short body timeouts; very large TTS payloads on slow links; disk filling up from previous media files; provider-side 5xx that manifests as a broken chunked body.","solutions":["Retry the request once — transient stream breaks are the most common cause","Shorten the input text and retry; long inputs stretch the transfer window","Check disk space (df -h) to rule out ENOSPC during the write","Verify provider health directly (curl the TTS endpoint with the same payload) and check quota/billing","If behind a proxy, raise its response/stream timeout or bypass it for the TTS host"],"exampleFix":"// before: single-shot synthesis of a huge text blob\nref, err := tts.SynthesizeToStore(ctx, veryLongText, ...)\n\n// after: chunk long input and retry transient stream failures\nvar ref string\nerr := retry(2, func() error {\n    r, e := tts.SynthesizeToStore(ctx, truncate(text, 4000), ...)\n    if e != nil { return e }\n    ref = r\n    return nil\n})","handlingStrategy":"retry","validationCode":"func ttsPayloadSafe(text string) error {\n    if strings.TrimSpace(text) == \"\" {\n        return fmt.Errorf(\"text is required\")\n    }\n    if len(text) > 4000 { // provider-dependent cap\n        return fmt.Errorf(\"text too long (%d bytes), chunk it\", len(text))\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"var ref string\nerr := retryN(2, 750*time.Millisecond, func() error {\n    r, e := tts.SynthesizeToStore(ctx, text, ch, chatID, filename)\n    if e != nil && strings.Contains(e.Error(), \"failed to write tts audio\") {\n        return e // transient stream break: retry\n    }\n    if e != nil { return retryStop{e} } // non-stream errors: do not retry\n    ref = r\n    return nil\n})","preventionTips":["Chunk long TTS inputs instead of one giant synthesis","Keep provider traffic off aggressively short-timeout proxies","Retry stream-write failures once or twice before surfacing them","Watch disk space so ENOSPC never masquerades as a network error"],"tags":["go","network","tts","io","retry"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}