{"record":{"id":"c78c9e4de4e29f4e","repo":"ruby/rubygems","slug":"can-t-be-updated-because-file-system-is-read-only","errorCode":null,"errorMessage":"can't be updated because file system is read-only","messagePattern":"can't be updated because file system is read-only","errorType":"exception","errorClass":"ProductionError","httpStatus":null,"severity":"error","filePath":"lib/bundler/definition.rb","lineNumber":447,"sourceCode":"              \"Your lockfile needs to be updated, but it can't be because frozen mode is set.\\n\\n\" \\\n              \"Run `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control.\"\n        raise ProductionError, msg\n      end\n\n      # Convert to \\r\\n if the existing lock has them, i.e., Windows with\n      # `git config core.autocrlf=true`. Detect from the bytes on disk because\n      # reading in text mode strips carriage returns on Windows, which would\n      # otherwise defeat this check and rewrite a `\\r\\n` lockfile with `\\n`.\n      if File.exist?(file) && SharedHelpers.filesystem_access(file, :read) {|p| File.binread(p).include?(\"\\r\\n\") }\n        contents.gsub!(/\\n/, \"\\r\\n\")\n      end\n\n      begin\n        SharedHelpers.filesystem_access(file) do |p|\n          File.open(p, \"wb\") {|f| f.puts(contents) }\n        end\n      rescue ReadOnlyFileSystemError\n        raise ProductionError, lockfile_changes_summary(\"file system is read-only\")\n      end\n    end\n\n    def locked_ruby_version\n      return unless ruby_version\n      if @unlocking_ruby || !@locked_ruby_version\n        Bundler::RubyVersion.system\n      else\n        @locked_ruby_version\n      end\n    end\n\n    def locked_ruby_version_object\n      return unless @locked_ruby_version\n      @locked_ruby_version_object ||= begin\n        unless version = RubyVersion.from_string(@locked_ruby_version)\n          raise LockfileError, \"The Ruby version #{@locked_ruby_version} from \" \\\n            \"#{@lockfile} could not be parsed. \" \\","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/ruby/rubygems/blob/86cbb817a38ce8477b181c17467a703ded3f2be8/lib/bundler/definition.rb#L429-L465","documentation":"While writing the resolved lockfile, Definition#write_lock opens the file inside SharedHelpers.filesystem_access; an Errno::EROFS from the operating system is normalized to ReadOnlyFileSystemError and re-raised as ProductionError with a lockfile-changes summary ending in file system is read-only (lib/bundler/definition.rb:447). It means resolution changed the lockfile but the storage cannot accept writes: a read-only container filesystem, an immutable production image, or (via the same filesystem_access wrapper) missing write permission on the directory. An up-to-date lockfile triggers no write, so this fires only when a change was actually needed.","triggerScenarios":"`bundle install` in a container started with docker --read-only or a Kubernetes readOnlyRootFilesystem pod while Gemfile.lock is stale; runtime re-locking in distroless images; bundle commands on NFS or overlay mounts mounted read-only; an unwritable app directory even on a writable filesystem.","commonSituations":"Security-hardened deployments run app volumes read-only; an image build changed the Gemfile but shipped a stale lockfile so the entrypoint re-resolves at boot; sidecars running bundle at startup inside read-only containers.","solutions":["Move locking to build time: run `bundle install` during the image build and ship the current Gemfile.lock so runtime needs no write","If the runtime must lock, give it a writable layer: mount a writable volume on the app dir or drop --read-only","Fix directory permissions when the filesystem is writable but the user lacks write access","Check writability before bundler runs: `ruby -e \"puts File.writable?(Dir.pwd)\"`"],"exampleFix":"# before\n$ docker run --read-only myapp bundle install\n# => can't be updated because file system is read-only\n\n# after (lock at build time)\n# Dockerfile: COPY Gemfile Gemfile.lock ./  then  RUN bundle install\n$ docker run --read-only myapp bundle exec puma","handlingStrategy":"validation","validationCode":"lockfile = Bundler.default_lockfile\nabort \"directory not writable\" unless File.writable?(File.dirname(lockfile))\nabort \"lockfile unwritable\" if lockfile.exist? && !File.writable?(lockfile)","typeGuard":null,"tryCatchPattern":"begin\n  Bundler.definition.lock(Bundler.default_lockfile)\nrescue Bundler::ProductionError => e\n  warn e.message if e.message.include?(\"read-only\")\n  # ship a pre-generated lockfile instead of locking here\nend","preventionTips":["Treat Gemfile.lock as a build artifact: resolve at build time, never at runtime","Test images with docker run --read-only in CI","Keep runtime filesystems read-only AND lockfiles current; the error means one of the two slipped"],"tags":["bundler","read-only-filesystem","lockfile","docker","permissions"],"backgroundTag":"read-only-filesystem","analyzedSha":"86cbb817a38ce8477b181c17467a703ded3f2be8","analyzedAt":"2026-08-23T06:27:48.159Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}