{"record":{"id":"3d2783e8891da36c","repo":"Hmbown/CodeWhale","slug":"ffmpeg-mux-failed-r-stderr-slice-0-300","errorCode":null,"errorMessage":"ffmpeg mux failed: ${(r.stderr || \"\").slice(0, 300)}","messagePattern":"ffmpeg mux failed: (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/harmonyos.mjs","lineNumber":315,"sourceCode":"        await tick();\n        throwIfAborted(signal);\n        if (rec.stopped) throw new ExecError(\"Harmony recording closed during startup\");\n        rec.timer = setInterval(tick, Math.max(150, intervalMs));\n      } catch (err) { await stopFrames(); throw err; }\n      finally { signal?.removeEventListener(\"abort\", abort); }\n      return { id, mode: \"snapshot-series\", intervalMs, startedAt, note: \"no HarmonyOS CLI screen recorder; frames are muxed into mp4 on stop\" };\n    },\n    recordingStop: async ({ id }) => {\n      if (!recording || recording.id !== id) throw new ExecError(`unknown recording \"${id}\"`);\n      const { dir, seq, startedAt, intervalMs } = await stopFrames();\n      const dirOut = process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), \".codewhale-cu\", \"recordings\");\n      fs.mkdirSync(dirOut, { recursive: true });\n      const out = path.join(dirOut, `rec-${id}.mp4`);\n      const fps = Math.max(1, Math.min(15, Math.round(1000 / Math.max(150, intervalMs))));\n      const r = await run(\"ffmpeg\", [\"-y\", \"-loglevel\", \"error\", \"-framerate\", String(fps), \"-i\", path.join(dir, \"f%05d.jpeg\"), \"-c:v\", \"libx264\", \"-pix_fmt\", \"yuv420p\", out], { timeoutMs: 180_000 });\n      const bytes = fs.existsSync(out) ? fs.statSync(out).size : 0;\n      try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}\n      if (r.code !== 0) throw new ExecError(`ffmpeg mux failed: ${(r.stderr || \"\").slice(0, 300)}`, r);\n      return { id, mode: \"snapshot-series\", frames: seq, fps, file: out, bytes, startedAt, stoppedAt: new Date().toISOString() };\n    },\n    recordingStatus: ({ id }) => recording && recording.id === id\n      ? { id, running: true, mode: \"snapshot-series\", frames: recording.seq, startedAt: recording.startedAt }\n      : { id, running: false },\n    recordingList: async () => {\n      const dir = process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), \".codewhale-cu\", \"recordings\");\n      const out = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => /\\.(mp4|mov|jpeg|png)$/i.test(f)).map((f) => {\n        const st = fs.statSync(path.join(dir, f));\n        return { file: path.join(dir, f), bytes: st.size, modifiedAt: st.mtime.toISOString() };\n      }).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, 50) : [];\n      return { dir, recordings: out, running: recording ? [recording.id] : [] };\n    },\n  };\n}\n\nexport default { create };\n","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/harmonyos.mjs#L297-L333","documentation":"After frames are captured, `recordingStop` invokes ffmpeg to mux the snapshot-series JPEGs into an mp4 (libx264, yuv420p, up to 180s). If ffmpeg exits non-zero, the backend throws with the first 300 bytes of ffmpeg's stderr and passes the raw result along, so the failure cause comes from ffmpeg itself (bad frames, unsupported codec build, disk full, etc.).","triggerScenarios":"Calling `recordingStop({ id })` where the captured frame sequence cannot be encoded: zero/corrupt frames, an ffmpeg build without libx264, disk full or unwritable output dir, or ffmpeg timing out (backends/harmonyos.mjs:315).","commonSituations":"Recording a session where the screen never produced a frame; minimal ffmpeg builds lacking libx264; recordings directory on a read-only mount; intervalMs so high that fps rounds to an invalid value.","solutions":["Read the stderr excerpt in the error message to identify the ffmpeg failure and address it directly.","Verify the installed ffmpeg supports libx264 (`ffmpeg -encoders | grep libx264`) and reinstall a full build if not.","Check disk space and write permissions on the recordings directory (CODEWHALE_CU_RECORDINGS_DIR or ~/.codewhale-cu/recordings).","Retry the recording with a smaller intervalMs and confirm frames are being captured (recordingStatus shows frames count)."],"exampleFix":"// before\n// error surfaced: ffmpeg mux failed: Unknown encoder 'libx264'\n// after\n// install a full ffmpeg build, e.g.: apt-get install -y ffmpeg (includes libx264)\nconst result = await backend.recordingStop({ id });","handlingStrategy":"try-catch","validationCode":"import { execFileSync } from \"node:child_process\";\nconst enc = execFileSync(\"ffmpeg\", [\"-hide_banner\", \"-encoders\"], { encoding: \"utf8\" });\nif (!enc.includes(\"libx264\")) throw new Error(\"ffmpeg lacks libx264\");","typeGuard":"null","tryCatchPattern":"try { await backend.recordingStop({ id }); }\ncatch (e) {\n  if (String(e.message).startsWith(\"ffmpeg mux failed\")) {\n    console.error(\"ffmpeg stderr:\", String(e.message).slice(\"ffmpeg mux failed: \".length));\n    // inspect disk space, codec support, frame files, then retry recording\n  }\n  throw e;\n}","preventionTips":["Verify ffmpeg has libx264 before recording workflows","Monitor disk space in the recordings directory","Ensure recordings dir (CODEWHALE_CU_RECORDINGS_DIR) is writable","Keep intervalMs within sane bounds so fps is valid"],"tags":["ffmpeg","mux-failure","recording","external-process"],"backgroundTag":"command-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}