diff --git a/app/models/packages/conan/file_metadatum.rb b/app/models/packages/conan/file_metadatum.rb
index c8de1df5effb242a19590576f20141ec0997bdf4..65ff3e0e156e04a3c886d6aeca742da52811ec42 100644
--- a/app/models/packages/conan/file_metadatum.rb
+++ b/app/models/packages/conan/file_metadatum.rb
@@ -23,6 +23,7 @@ class Packages::Conan::FileMetadatum < ApplicationRecord
   PACKAGE_FILES = ::Gitlab::Regex::Packages::CONAN_PACKAGE_FILES
   PACKAGE_BINARY = 'conan_package.tgz'
   CONAN_MANIFEST = 'conanmanifest.txt'
+  CONANINFO_TXT = 'conaninfo.txt'
 
   def recipe_revision_value
     recipe_revision&.revision || DEFAULT_REVISION
diff --git a/app/services/packages/conan/create_package_file_service.rb b/app/services/packages/conan/create_package_file_service.rb
index e1c35b4e156a609fcef26bb8028a2073511374f9..98bd50b1e391a1264e7127d5f9208dab2f055c41 100644
--- a/app/services/packages/conan/create_package_file_service.rb
+++ b/app/services/packages/conan/create_package_file_service.rb
@@ -34,6 +34,10 @@ def execute
           package_file.save!
         end
 
+        if package_file.file_name == ::Packages::Conan::FileMetadatum::CONANINFO_TXT && Feature.enabled?(:parse_conan_metadata_on_upload, Project.actor_from_id(package_file.project_id))
+          ::Packages::Conan::ProcessPackageFileWorker.perform_async(package_file.id)
+        end
+
         ServiceResponse.success(payload: { package_file: package_file })
       rescue ActiveRecord::RecordInvalid => e
         ServiceResponse.error(message: e.message, reason: :invalid_package_file)
diff --git a/app/services/packages/conan/metadata_extraction_service.rb b/app/services/packages/conan/metadata_extraction_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1d8a78cce537ee747b4c33006a4a51fef9cfaf63
--- /dev/null
+++ b/app/services/packages/conan/metadata_extraction_service.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+module Packages
+  module Conan
+    class MetadataExtractionService
+      include Gitlab::Utils::StrongMemoize
+
+      ExtractionError = Class.new(StandardError)
+
+      SECTION_HEADER_REGEX = /^\[(\w{1,50})\]$/
+      RECIPE_HASH_SECTION = 'recipe_hash'
+      ALLOWED_SECTIONS = %w[requires recipe_hash options settings].freeze
+
+      def initialize(package_file)
+        @package_file = package_file
+      end
+
+      def execute
+        result = parse_conaninfo_content
+
+        package_reference.update!(info: result)
+        ServiceResponse.success
+      rescue ActiveRecord::RecordInvalid => e
+        error_message = e.record.errors.full_messages.first
+        raise ExtractionError, "conaninfo.txt file too large" if error_message.include?("Info conaninfo is too large")
+
+        raise ExtractionError, "conaninfo.txt metadata failedto be saved: #{error_message}"
+      end
+
+      private
+
+      attr_reader :package_file
+
+      def parse_conaninfo_content
+        package_file.file.use_open_file(unlink_early: false) do |open_file|
+          parse_file_contents(File.open(open_file.file_path))
+        end
+      rescue StandardError => e
+        raise ExtractionError, "Error while parsing conaninfo.txt: #{e.message}"
+      end
+
+      def parse_file_contents(file)
+        result = {}
+        current_section = current_data = nil
+
+        file.each do |line|
+          line = line.strip
+          next if line.empty?
+
+          section_name = section_name_from(line)
+          if section_name
+            if current_section && current_data && allowed_section?(current_section)
+              result[current_section] = current_data
+            end
+
+            current_section = section_name
+            current_data = nil
+          elsif current_section && allowed_section?(current_section)
+            validate_recipe_hash_section(current_section, current_data)
+            current_data = process_section_line(current_section, line, current_data)
+          end
+        end
+
+        result[current_section] = current_data if current_section && current_data && allowed_section?(current_section)
+        result
+      end
+
+      def validate_recipe_hash_section(section, data)
+        return unless section == RECIPE_HASH_SECTION && data
+
+        raise ExtractionError, 'The recipe_hash section cannot have multiple lines'
+      end
+
+      def process_section_line(section, line, data)
+        return line if section == RECIPE_HASH_SECTION
+        return process_key_value_line(line, data) if line.include?('=')
+
+        process_array_line(line, data)
+      end
+
+      def process_key_value_line(line, data)
+        data ||= {}
+        key, value = line.split('=', 2).map(&:strip)
+        raise ExtractionError, "Invalid key-value line: #{line}" if key.empty? || value.empty?
+
+        data[key] = value
+        data
+      end
+
+      def process_array_line(line, data)
+        data ||= []
+        data << line
+      end
+
+      def allowed_section?(section)
+        strong_memoize_with(:allowed_section, section) do
+          ALLOWED_SECTIONS.include?(section)
+        end
+      end
+
+      def section_name_from(line)
+        section = line.match(SECTION_HEADER_REGEX)&.captures&.first
+        raise ExtractionError, "Invalid section header: #{line}" if section.nil? && line.start_with?('[')
+
+        section
+      end
+
+      def package_reference
+        package_file.conan_file_metadatum.package_reference
+      end
+    end
+  end
+end
diff --git a/app/validators/json_schemas/conan_package_info.json b/app/validators/json_schemas/conan_package_info.json
index 3e2df2d8bc0fc12e6df93402550c3aaf9b07f9da..8f1607329280f87d844da7bd449bdd73f69c6afc 100644
--- a/app/validators/json_schemas/conan_package_info.json
+++ b/app/validators/json_schemas/conan_package_info.json
@@ -1,33 +1,28 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "description": "Conan Info Schema",
-  "oneOf": [
-    {
+  "type": "object",
+  "properties": {
+    "recipe_hash": {
+      "type": "string"
+    },
+    "settings": {
       "type": "object",
-      "additionalProperties": false
+      "additionalProperties": {
+        "type": "string"
+      }
     },
-    {
+    "options": {
       "type": "object",
-      "properties": {
-        "settings": {
-          "type": "object"
-        },
-        "requires": {
-          "type": "array",
-          "items": {
-            "type": "string"
-          }
-        },
-        "options": {
-          "type": "object"
-        }
-      },
-      "required": [
-        "settings",
-        "requires",
-        "options"
-      ],
-      "additionalProperties": true
+      "additionalProperties": {
+        "type": "string"
+      }
+    },
+    "requires": {
+      "type": "array",
+      "items": {
+        "type": "string"
+      }
     }
-  ]
+  }
 }
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 0140d207b7638b2cf9427023dca43d5892947719..4d3d0fa852dcd86f644d3b7db2567d1d7e2bb316 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2134,6 +2134,16 @@
   :tags:
   - :cronjob_child
   :queue_namespace: :package_cleanup
+- :name: package_repositories:packages_conan_process_package_file
+  :worker_name: Packages::Conan::ProcessPackageFileWorker
+  :feature_category: :package_registry
+  :has_external_dependencies: false
+  :urgency: :low
+  :resource_boundary: :unknown
+  :weight: 1
+  :idempotent: true
+  :tags: []
+  :queue_namespace: :package_repositories
 - :name: package_repositories:packages_debian_generate_distribution
   :worker_name: Packages::Debian::GenerateDistributionWorker
   :feature_category: :package_registry
diff --git a/app/workers/concerns/packages/error_handling.rb b/app/workers/concerns/packages/error_handling.rb
index a1b1b58aad52eb3094b4a4c0a8371517b85f212b..1ca125ee14fbe4ebd5fb77e70dfb6ec48da00961 100644
--- a/app/workers/concerns/packages/error_handling.rb
+++ b/app/workers/concerns/packages/error_handling.rb
@@ -17,7 +17,8 @@ module ErrorHandling
       ::Packages::Rubygems::ProcessGemService::ExtractionError,
       ::Packages::Rubygems::ProcessGemService::InvalidMetadataError,
       ::Packages::Npm::ProcessPackageFileService::ExtractionError,
-      ::Packages::Npm::CheckManifestCoherenceService::MismatchError
+      ::Packages::Npm::CheckManifestCoherenceService::MismatchError,
+      ::Packages::Conan::MetadataExtractionService::ExtractionError
     ].freeze
 
     def process_package_file_error(package_file:, exception:, extra_log_payload: {})
diff --git a/app/workers/packages/conan/process_package_file_worker.rb b/app/workers/packages/conan/process_package_file_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0c451e2de91ceeab79c037949f698e9184697a78
--- /dev/null
+++ b/app/workers/packages/conan/process_package_file_worker.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Packages
+  module Conan
+    class ProcessPackageFileWorker
+      include ApplicationWorker
+
+      data_consistency :sticky
+      queue_namespace :package_repositories
+      feature_category :package_registry
+      deduplicate :until_executed
+      idempotent!
+
+      def perform(package_file_id)
+        package_file = ::Packages::PackageFile.find_by_id(package_file_id)
+        return unless package_file
+
+        ::Packages::Conan::MetadataExtractionService.new(package_file).execute
+      rescue StandardError => exception
+        logger.warn(
+          message: "Error processing conaninfo.txt file",
+          error: exception.message,
+          package_file: package_file.id,
+          project_id: package_file.project_id,
+          package_name: package_file.package.name,
+          package_version: package_file.package.version
+        )
+      end
+    end
+  end
+end
diff --git a/config/feature_flags/gitlab_com_derisk/parse_conan_metadata_on_upload.yml b/config/feature_flags/gitlab_com_derisk/parse_conan_metadata_on_upload.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ab0715d3e404bba02f692ab954db674e13662df9
--- /dev/null
+++ b/config/feature_flags/gitlab_com_derisk/parse_conan_metadata_on_upload.yml
@@ -0,0 +1,9 @@
+---
+name: parse_conan_metadata_on_upload
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/33892
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178728
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/523693
+milestone: '17.10'
+group: group::package registry
+type: gitlab_com_derisk
+default_enabled: false
diff --git a/spec/factories/packages/conan/package_references.rb b/spec/factories/packages/conan/package_references.rb
index 792cd43b097db614c66252b26296c35214138186..dc9553b2b0445d3bf02c12898ddca5af00130015 100644
--- a/spec/factories/packages/conan/package_references.rb
+++ b/spec/factories/packages/conan/package_references.rb
@@ -9,7 +9,7 @@
       {
         settings: { os: 'Linux', arch: 'x86_64' },
         requires: ['libA/1.0@user/testing'],
-        options: { fPIC: true },
+        options: { fPIC: 'True' },
         otherProperties: 'some_value'
       }
     end
diff --git a/spec/factories/packages/package_files.rb b/spec/factories/packages/package_files.rb
index 7c178322681e67b7db15068210a4763159623ff3..efbc8afb55af7d22eb5091a4e03419abf14e67ce 100644
--- a/spec/factories/packages/package_files.rb
+++ b/spec/factories/packages/package_files.rb
@@ -84,11 +84,16 @@
           end
         end
 
-        file_fixture { 'spec/fixtures/packages/conan/package_files/conaninfo.txt' }
+        transient do
+          conaninfo_fixture { 'conaninfo.txt' }
+          fixture_path { "spec/fixtures/packages/conan/package_files/#{conaninfo_fixture}" }
+        end
+
+        file_fixture { fixture_path }
         file_name { 'conaninfo.txt' }
         file_sha1 { 'be93151dc23ac34a82752444556fe79b32c7a1ad' }
         file_md5 { '12345abcde' }
-        size { 400.kilobytes }
+        size { File.size(fixture_path) }
       end
 
       trait(:conan_package) do
diff --git a/spec/fixtures/packages/conan/package_files/conaninfo.txt b/spec/fixtures/packages/conan/package_files/conaninfo.txt
index 2a02515a19b36d9b3a29f610b67a683f585f5024..2da2c19cf9460b323cee00434a1eff3a5e12ef6f 100644
--- a/spec/fixtures/packages/conan/package_files/conaninfo.txt
+++ b/spec/fixtures/packages/conan/package_files/conaninfo.txt
@@ -7,7 +7,9 @@
     os=Macos
 
 [requires]
-
+    cli11/1.Y.Z
+    fmt/7.Y.Z
+    prometheus-cpp/1.Y.Z
 
 [options]
     shared=False
@@ -21,7 +23,13 @@
     os=Macos
 
 [full_requires]
-
+    civetweb/1.15:77e8df9f2be98ef80d2a9f31ea49eb14597b20b0
+    cli11/1.9.1:5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
+    fmt/7.1.3:80138d4a58def120da0b8c9199f2b7a4e464a85b
+    libcurl/7.86.0:a097455223234e250d01a2687cf7c15446fbd5d5
+    openssl/1.1.1s:6841fe8f0f22f6fa260da36a43a94ab525c7ed8d
+    prometheus-cpp/1.1.0:b39e1754fc610f750a6d595455854696692ec5bc
+    zlib/1.2.13:6841fe8f0f22f6fa260da36a43a94ab525c7ed8d
 
 [full_options]
     shared=False
@@ -30,4 +38,5 @@
     b4b91125b36b40a7076a98310588f820
 
 [env]
-
+    CC=clang-10
+    CXX=clang++-10
diff --git a/spec/fixtures/packages/conan/package_files/conaninfo_invalid_line.txt b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_line.txt
new file mode 100644
index 0000000000000000000000000000000000000000..156f3a591434d8eb029114698c921fdcc01e8a95
--- /dev/null
+++ b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_line.txt
@@ -0,0 +1,7 @@
+[settings]
+    arch=x86_64
+    test=
+    another_test=123
+
+[recipe_hash]
+    abc123def456
diff --git a/spec/fixtures/packages/conan/package_files/conaninfo_invalid_recipe_hash.txt b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_recipe_hash.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1d6b9d901bcb67324dc95a0e2e4428aea0694d52
--- /dev/null
+++ b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_recipe_hash.txt
@@ -0,0 +1,9 @@
+[settings]
+    arch=x86_64
+
+[recipe_hash]
+    abc123def456
+    another_hash_line_not_allowed
+
+[env]
+    CC=clang-10
diff --git a/spec/fixtures/packages/conan/package_files/conaninfo_invalid_section.txt b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_section.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9252abf88aab512f5950b85c83dc56b03f36d226
--- /dev/null
+++ b/spec/fixtures/packages/conan/package_files/conaninfo_invalid_section.txt
@@ -0,0 +1,24 @@
+[requires]
+some_package/1.0.0
+
+[options]
+shared=False
+
+[full_settings]
+    arch=x86_64
+    build_type=Release
+
+[full_requires]
+some_package/1.0.0
+
+[full_options]
+    shared=False
+
+[recipe_hash]
+    a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
+
+[missing_bracket
+    this_section_is_not_properly_closed
+
+[env]
+    SOME_ENV_VAR=value
diff --git a/spec/fixtures/packages/conan/package_files/conaninfo_minimal.txt b/spec/fixtures/packages/conan/package_files/conaninfo_minimal.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a034f9243fea30bcadf0b5694e4e69714601a0fa
--- /dev/null
+++ b/spec/fixtures/packages/conan/package_files/conaninfo_minimal.txt
@@ -0,0 +1,21 @@
+[settings]
+    arch=x86_64
+    build_type=Release
+    compiler=apple-clang
+    compiler.libcxx=libc++
+    compiler.version=10.0
+    os=Macos
+
+[requires]
+
+[options]
+
+[full_settings]
+
+[full_requires]
+
+[full_options]
+
+[recipe_hash]
+
+[env]
diff --git a/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo.json b/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo.json
new file mode 100644
index 0000000000000000000000000000000000000000..e090f09a98a070b4277a620ac183bf4a5a01337c
--- /dev/null
+++ b/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo.json
@@ -0,0 +1,19 @@
+{
+  "settings": {
+    "arch": "x86_64",
+    "build_type": "Release",
+    "compiler": "apple-clang",
+    "compiler.libcxx": "libc++",
+    "compiler.version": "10.0",
+    "os": "Macos"
+  },
+  "requires": [
+    "cli11/1.Y.Z",
+    "fmt/7.Y.Z",
+    "prometheus-cpp/1.Y.Z"
+  ],
+  "options": {
+    "shared": "False"
+  },
+  "recipe_hash": "b4b91125b36b40a7076a98310588f820"
+}
diff --git a/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo_minimal.json b/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo_minimal.json
new file mode 100644
index 0000000000000000000000000000000000000000..f13ad503c28b779d0c183ba2d114c15aca2deef4
--- /dev/null
+++ b/spec/fixtures/packages/conan/parsed_conaninfo/conaninfo_minimal.json
@@ -0,0 +1,10 @@
+{
+  "settings": {
+    "arch": "x86_64",
+    "build_type": "Release",
+    "compiler": "apple-clang",
+    "compiler.libcxx": "libc++",
+    "compiler.version": "10.0",
+    "os": "Macos"
+  }
+}
diff --git a/spec/models/packages/conan/package_reference_spec.rb b/spec/models/packages/conan/package_reference_spec.rb
index 6ad31a00f495f14b5e66a930ee6a1bd1282b8cec..a116879327c955acb25bbbd880b9f7499fe57425 100644
--- a/spec/models/packages/conan/package_reference_spec.rb
+++ b/spec/models/packages/conan/package_reference_spec.rb
@@ -118,12 +118,12 @@
       end
 
       context 'with invalid conan info' do
-        let(:info) { { invalid_field: 'some_value' } }
+        let(:info) { { settings: 'incorrect_value' } }
 
         it 'is invalid', :aggregate_failures do
           expect(package_reference).not_to be_valid
           expect(package_reference.errors[:info]).to include(
-            'object at root is missing required properties: settings, requires, options')
+            'value at `/settings` is not an object')
         end
       end
 
diff --git a/spec/services/packages/conan/create_package_file_service_spec.rb b/spec/services/packages/conan/create_package_file_service_spec.rb
index fc19aaabe8aadd37a2832da6a7e39dea9b9e53b9..063ed6be21121ff50b68eb53733644a6cb0e9010 100644
--- a/spec/services/packages/conan/create_package_file_service_spec.rb
+++ b/spec/services/packages/conan/create_package_file_service_spec.rb
@@ -213,5 +213,68 @@
         expect { response }.not_to change { Packages::Conan::PackageReference.count }
       end
     end
+
+    context 'queueing the conan package file processing worker' do
+      before do
+        allow(::Packages::Conan::ProcessPackageFileWorker).to receive(:perform_async)
+      end
+
+      let(:params) do
+        {
+          file_name: file_name,
+          'file.md5': '12345',
+          'file.sha1': '54321',
+          'file.size': '128',
+          'file.type': 'txt',
+          recipe_revision: '0',
+          package_revision: '0',
+          conan_package_reference: '123456789',
+          conan_file_type: :package_file
+        }.with_indifferent_access
+      end
+
+      context 'when the filename is conaninfo.txt' do
+        let(:file_name) { 'conaninfo.txt' }
+
+        let(:file) do
+          fixture_file_upload('spec/fixtures/packages/conan/package_files/conaninfo.txt', 'text/plain')
+        end
+
+        context 'when the feature flag is enabled' do
+          before do
+            stub_feature_flags(parse_conan_metadata_on_upload: true)
+          end
+
+          it 'queues the Conan package file processing worker' do
+            expect(response).to be_success
+            expect(::Packages::Conan::ProcessPackageFileWorker).to have_received(:perform_async)
+              .with(response[:package_file].id)
+          end
+        end
+
+        context 'when the feature flag is disabled' do
+          before do
+            stub_feature_flags(parse_conan_metadata_on_upload: false)
+          end
+
+          it 'does not queue the Conan package file processing worker' do
+            expect(response).to be_success
+            expect(::Packages::Conan::ProcessPackageFileWorker).not_to have_received(:perform_async)
+          end
+        end
+      end
+
+      context 'when the filename is not conaninfo.txt' do
+        include_context 'with temp file setup'
+
+        let(:file_name) { 'not_conaninfo.txt' }
+
+        it 'does not queue the Conan package file processing worker' do
+          expect(::Packages::Conan::ProcessPackageFileWorker).not_to receive(:perform_async)
+
+          response
+        end
+      end
+    end
   end
 end
diff --git a/spec/services/packages/conan/metadata_extraction_service_spec.rb b/spec/services/packages/conan/metadata_extraction_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d85785af2d72442c3104073b3011b1d6c6af2ee1
--- /dev/null
+++ b/spec/services/packages/conan/metadata_extraction_service_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Conan::MetadataExtractionService, feature_category: :package_registry do
+  using RSpec::Parameterized::TableSyntax
+
+  let_it_be(:package_reference) { create(:conan_package_reference, info: {}) }
+  let_it_be(:package_file) do
+    create(:conan_package_file, :conan_package_info, conan_package_reference: package_reference)
+  end
+
+  describe '#execute' do
+    subject(:service) { described_class.new(package_file).execute }
+
+    describe 'parsing conaninfo files' do
+      let(:expected_metadata) do
+        Gitlab::Json.parse(fixture_file_upload("spec/fixtures/packages/conan/parsed_conaninfo/#{expected_json}").read)
+      end
+
+      where(:conaninfo_fixture, :expected_json) do
+        'conaninfo.txt'         | 'conaninfo.json'
+        'conaninfo_minimal.txt' | 'conaninfo_minimal.json'
+      end
+
+      with_them do
+        before do
+          package_file.file = fixture_file_upload("spec/fixtures/packages/conan/package_files/#{conaninfo_fixture}")
+        end
+
+        it 'updates the package reference info', :aggregate_failures do
+          expect { service }
+            .to change { package_file.conan_file_metadatum.package_reference.reload.info }
+            .from({})
+            .to(expected_metadata)
+        end
+      end
+    end
+
+    context 'with database error' do
+      # rubocop:disable Layout/LineLength -- Required for formatting of table
+      where(:database_error_message, :expected_error_message) do
+        'Info conaninfo is too large. Maximum size is 20000 characters' | 'conaninfo.txt file too large'
+        'Test error'                                                    | 'conaninfo.txt metadata failedto be saved: Test error'
+      end
+      # rubocop:enable Layout/LineLength
+
+      with_them do
+        before do
+          allow(package_reference).to receive(:update!).and_raise(ActiveRecord::RecordInvalid.new(package_reference))
+          allow(package_reference).to receive_message_chain(:errors,
+            :full_messages).and_return([database_error_message])
+        end
+
+        it 'raises ExtractionError and does not update package reference info' do
+          expect { service }
+            .to raise_error(described_class::ExtractionError, expected_error_message)
+            .and not_change { package_reference.reload.info }
+        end
+      end
+    end
+
+    context 'with invalid conaninfo.txt' do
+      # rubocop:disable Layout/LineLength -- Required for formatting of table
+      where(:conaninfo_fixture, :expected_error) do
+        'conaninfo_invalid_line.txt'        | 'Error while parsing conaninfo.txt: Invalid key-value line: test='
+        'conaninfo_invalid_recipe_hash.txt' | 'Error while parsing conaninfo.txt: The recipe_hash section cannot have multiple lines'
+        'conaninfo_invalid_section.txt'     | 'Error while parsing conaninfo.txt: Invalid section header: [missing_bracket'
+      end
+      # rubocop:enable Layout/LineLength
+
+      with_them do
+        before do
+          package_file.file = fixture_file_upload("spec/fixtures/packages/conan/package_files/#{conaninfo_fixture}")
+        end
+
+        it 'raises ExtractionError and does not update package reference info' do
+          expect { service }
+            .to raise_error(described_class::ExtractionError, expected_error)
+            .and not_change { package_file.conan_file_metadatum.package_reference.reload.info }
+        end
+      end
+    end
+  end
+end
diff --git a/spec/workers/packages/conan/process_package_file_worker_spec.rb b/spec/workers/packages/conan/process_package_file_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..95b248bee9f088489721dca452421b15ac791d52
--- /dev/null
+++ b/spec/workers/packages/conan/process_package_file_worker_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Conan::ProcessPackageFileWorker, type: :worker, feature_category: :package_registry do
+  let_it_be(:package_file) { create(:conan_package_file, :conan_package_info) }
+
+  let(:worker) { described_class.new }
+  let(:package_file_id) { package_file.id }
+
+  describe "#perform" do
+    subject(:perform) { worker.perform(package_file_id) }
+
+    it_behaves_like 'an idempotent worker' do
+      let(:job_args) { package_file_id }
+    end
+
+    it_behaves_like 'worker with data consistency', described_class, data_consistency: :sticky
+
+    it 'has :until_executed deduplicate strategy' do
+      expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
+    end
+
+    context 'with existing package file' do
+      it 'calls the MetadataExtractionService' do
+        expect_next_instance_of(::Packages::Conan::MetadataExtractionService, package_file) do |service|
+          expect(service).to receive(:execute)
+        end
+
+        perform
+      end
+
+      context 'when service raises an error' do
+        let(:exception) { ::Packages::Conan::MetadataExtractionService::ExtractionError.new('test error') }
+        let(:logger) { instance_double(::Logger) }
+
+        before do
+          allow_next_instance_of(::Packages::Conan::MetadataExtractionService) do |service|
+            allow(service).to receive(:execute).and_raise(exception)
+          end
+          allow(logger).to receive(:warn)
+          allow(worker).to receive(:logger).and_return(logger)
+        end
+
+        it 'processes the error through error handling concern' do
+          expect(logger).to receive(:warn).with(
+            message: "Error processing conaninfo.txt file",
+            error: exception.message,
+            package_file: package_file.id,
+            project_id: package_file.project_id,
+            package_name: package_file.package.name,
+            package_version: package_file.package.version
+          )
+
+          perform
+        end
+      end
+    end
+
+    context 'with a non-existing package file' do
+      let(:package_file_id) { non_existing_record_id }
+
+      it 'does not call the service' do
+        expect(::Packages::Conan::MetadataExtractionService).not_to receive(:new)
+
+        perform
+      end
+    end
+  end
+end