Yalantis/uCrop · error · CImgIOException

save_ffmpeg_external(): Failed to save file

Error message

save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.

What it means

save_ffmpeg_external() pipes PPM frames to the external 'ffmpeg' binary and rebuilds the video. This error is thrown when the ffmpeg system command returns non-zero, or (second variant) when the output file does not exist afterwards, meaning ffmpeg failed or produced nothing.

Solutions

  1. Install ffmpeg or point cimg::ffmpeg_path() (CIMG_FFMPEG_PATH env var) to an existing binary
  2. Run 'ffmpeg -encoders' and pick a codec your build supports (e.g. mpeg4, mpeg2video)
  3. Check ffmpeg's stderr from the system call for the exact encoding failure
  4. Verify disk space and write permission on the target path

Example fix

// before
frames.save_ffmpeg_external("out.mp4", 25, "h264"); // no libx264 in ffmpeg build
// after
frames.save_ffmpeg_external("out.mp4", 25, "mpeg4"); // encoder present in stock builds
Defensive patterns

Strategy: fallback

Validate before calling

// C++
bool ffmpegOk = cimg::path_exists(cimg::ffmpeg_path());
bool writable = access(dir_of(fname), W_OK) == 0;
if (!ffmpegOk || !writable) useInternalWriterInstead();

Type guard

bool ffmpeg_available() { return cimg::path_exists(cimg::ffmpeg_path()); }

Try / catch

try { frames.save_ffmpeg_external(fname, fps, codec); }
catch (CImgIOException& e) { fprintf(stderr, "ffmpeg failed: %s", e.what()); frames.save_video(fname, fps, "mp4v"); }

Prevention

When it happens

Trigger: Calling save_ffmpeg_external() when the ffmpeg executable is missing/not at cimg::ffmpeg_path(), the requested -vcodec is unsupported by the installed ffmpeg, bad bitrate/fps arguments, disk full, or the output path is unwritable so no file appears after the run.

Common situations: Android/CI images without ffmpeg installed; ffmpeg builds lacking the chosen codec (e.g. h264/libx264 unavailable); codec name mismatch after upstream default change ('mpeg2video' fallback); SELinux blocking exec of ffmpeg.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08). Data as JSON: /api/errors/27a49cd47e942ff1. Report an issue: GitHub.

Appendix: source

Thrown at ucrop/src/main/jni/CImg.h:68711

          cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,frame);
          CImg<charT>::string(filename_tmp2).move_to(filenames);
          CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
          if (_src._width%2 || _src._height%2) // Force output to have an even number of columns and rows
            _src.assign(_src.get_resize(_src._width + (_src._width%2),_src._height + (_src._height%2),1,-100,0),false);
          if (_src._spectrum!=3) // Force output to be one slice, in color
            _src.assign(_src.get_resize(-100,-100,1,3),false);
          _src.save_pnm(filename_tmp2);
          ++frame;
        }
      }
      cimg_snprintf(command,command._width,
                    "\"%s\" -framerate %u -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
                    cimg::ffmpeg_path(),
                    fps,CImg<charT>::string(filename_tmp)._system_strescape().data(),
                    _codec,bitrate,fps,
                    CImg<charT>::string(filename)._system_strescape().data());
      if (cimg::system(command,cimg::ffmpeg_path())!=0)
        throw CImgIOException(_cimglist_instance
                              "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
                              cimglist_instance,
                              filename);
      if (!cimg::path_exists(filename))
        throw CImgIOException(_cimglist_instance
                              "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
                              cimglist_instance,
                              filename);
      cimglist_for(*this,l) std::remove(filenames[l]);
      return *this;
    }

    //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
    /**
       \param is_compressed tells if zlib compression must be used for serialization
       (this requires 'cimg_use_zlib' been enabled).
       \param header_size Reserve empty bytes as a starting header.
    **/

View on GitHub (pinned to f788b534b4)