{"record":{"id":"7eafbf0e4da8c62c","repo":"sipeed/picoclaw","slug":"failed-to-create-media-temp-dir-w","errorCode":null,"errorMessage":"failed to create media temp dir: %w","messagePattern":"failed to create media temp dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/audio/tts/tts.go","lineNumber":119,"sourceCode":"\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\"\n\t}\n\tif metaProvider, ok := stream.(ttsAudioMetaProvider); ok {\n\t\tif ext, ct := metaProvider.AudioFileMeta(); ext != \"\" && ct != \"\" {\n\t\t\tfileExt = ext\n\t\t\tcontentType = ct\n\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)","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/audio/tts/tts.go#L101-L137","documentation":"Thrown by the TTS tool path (pkg/audio/tts/tts.go:119) when os.MkdirAll(media.TempDir(), 0o700) fails before a synthesized audio stream is spooled to disk. The wrapped error is a *fs.PathError naming the exact directory and the syscall errno, so the root cause is always filesystem-level. Synthesis itself already succeeded; only scratch-space preparation failed, and no temp file was created.","triggerScenarios":"Calling the send_tts tool (SynthesizeToStore) when media.TempDir() points to: a path occupied by a regular file (ENOTDIR), a directory owned by another user or without write permission (EACCES), a read-only mount/container rootfs (EROFS), a missing parent that cannot be created (ENOENT), or a full disk (ENOSPC).","commonSituations":"media/temp_dir misconfigured in picoclaw config to an unwritable location; running in Docker with a read-only volume; HOME unset so the temp path resolves oddly; SELinux/AppArmor denying mkdir; sticky-bit /tmp dir created by a different uid with 0700.","solutions":["Read the wrapped PathError: it prints the failing path and errno (e.g. 'mkdir /var/media: permission denied') — fix that exact path first","Verify writability as the same user the bot runs as: mkdir -p <dir> && test -w <dir> && touch <dir>/probe","Point the media temp dir setting at a guaranteed-writable directory (e.g. os.TempDir() default or a dedicated writable volume) and retry","If containerized, mount a writable volume/emptyDir at the configured path instead of a read-only one","Check disk space and inodes: df -h <dir> && df -i <dir>"],"exampleFix":"// before: media temp dir points into a read-only mount\n// config: temp_dir = \"/var/picoclaw/media\"  (read-only volume)\n\n// after: point at a writable scratch location (or unset to use os.TempDir)\n// config: temp_dir = \"/var/tmp/picoclaw-media\"\n// verify before first use:\nif err := os.MkdirAll(media.TempDir(), 0o700); err != nil {\n    log.Printf(\"tts scratch dir unusable: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"// preflight before first TTS call / at service startup\nfunc checkTTSScratchDir() error {\n    dir := media.TempDir()\n    if err := os.MkdirAll(dir, 0o700); err != nil {\n        return fmt.Errorf(\"temp dir %s unusable: %w\", dir, err)\n    }\n    probe := filepath.Join(dir, \".probe\")\n    if err := os.WriteFile(probe, nil, 0o600); err != nil {\n        return fmt.Errorf(\"temp dir %s not writable: %w\", dir, err)\n    }\n    return os.Remove(probe)\n}","typeGuard":null,"tryCatchPattern":"if ref, err := tts.SynthesizeToStore(ctx, text, ch, chatID, name); err != nil {\n    var pathErr *fs.PathError\n    if errors.As(err, &pathErr) && strings.HasPrefix(pathErr.Path, media.TempDir()) {\n        // filesystem-level: surface config problem, do not retry\n        log.Printf(\"media temp dir problem at %s: %v\", pathErr.Path, pathErr.Err)\n    }\n    return err\n}","preventionTips":["Validate media.TempDir() writability at startup with a mkdir+touch probe","Keep the media temp dir on local writable storage, out of /tmp cleaner scope","Run the service under a user that owns the media temp dir","Monitor disk space on the volume hosting the temp dir"],"tags":["go","filesystem","tts","permissions","temp-files"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}