diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp
index d07521a090ac..fcbf8e557b10 100644
--- a/src/ipa/ipu3/algorithms/af.cpp
+++ b/src/ipa/ipu3/algorithms/af.cpp
@@ -420,7 +420,8 @@ bool Af::afIsOutOfFocus(IPAContext context)
  *
  * [1] Hill Climbing Algorithm, https://en.wikipedia.org/wiki/Hill_climbing
  */
-void Af::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Af::process(IPAContext &context,
+		 [[maybe_unused]] IPU3FrameContext &frameContext,
 		 const ipu3_uapi_stats_3a *stats)
 {
 	/* Evaluate the AF buffer length */
diff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h
index ccf015f3f8f5..7a5aeb1b6bf4 100644
--- a/src/ipa/ipu3/algorithms/af.h
+++ b/src/ipa/ipu3/algorithms/af.h
@@ -32,7 +32,7 @@ public:
 
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index 5bc64ae52214..92ea6249798d 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -183,13 +183,13 @@ utils::Duration Agc::filterExposure(utils::Duration exposureValue)
  * \param[in] yGain The gain calculated based on the relative luminance target
  * \param[in] iqMeanGain The gain calculated based on the relative luminance target
  */
-void Agc::computeExposure(IPAContext &context, IPAFrameContext *frameContext,
+void Agc::computeExposure(IPAContext &context, IPU3FrameContext &frameContext,
 			  double yGain, double iqMeanGain)
 {
 	const IPASessionConfiguration &configuration = context.configuration;
 	/* Get the effective exposure and gain applied on the sensor. */
-	uint32_t exposure = frameContext->sensor.exposure;
-	double analogueGain = frameContext->sensor.gain;
+	uint32_t exposure = frameContext.sensor.exposure;
+	double analogueGain = frameContext.sensor.gain;
 
 	/* Use the highest of the two gain estimates. */
 	double evGain = std::max(yGain, iqMeanGain);
@@ -323,7 +323,8 @@ double Agc::estimateLuminance(IPAActiveState &activeState,
  * Identify the current image brightness, and use that to estimate the optimal
  * new exposure and gain for the scene.
  */
-void Agc::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Agc::process(IPAContext &context,
+		  IPU3FrameContext &frameContext,
 		  const ipu3_uapi_stats_3a *stats)
 {
 	/*
diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h
index 105ae0f2aac6..96585f48933f 100644
--- a/src/ipa/ipu3/algorithms/agc.h
+++ b/src/ipa/ipu3/algorithms/agc.h
@@ -28,14 +28,14 @@ public:
 	~Agc() = default;
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
 	double measureBrightness(const ipu3_uapi_stats_3a *stats,
 				 const ipu3_uapi_grid_config &grid) const;
 	utils::Duration filterExposure(utils::Duration currentExposure);
-	void computeExposure(IPAContext &context, IPAFrameContext *frameContext,
+	void computeExposure(IPAContext &context, IPU3FrameContext &frameContext,
 			     double yGain, double iqMeanGain);
 	double estimateLuminance(IPAActiveState &activeState,
 				 const ipu3_uapi_grid_config &grid,
diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp
index 704267222a31..45f8e5f29119 100644
--- a/src/ipa/ipu3/algorithms/awb.cpp
+++ b/src/ipa/ipu3/algorithms/awb.cpp
@@ -387,7 +387,8 @@ void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats)
 /**
  * \copydoc libcamera::ipa::Algorithm::process
  */
-void Awb::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void Awb::process(IPAContext &context,
+		  [[maybe_unused]] IPU3FrameContext &frameContext,
 		  const ipu3_uapi_stats_3a *stats)
 {
 	calculateWBGains(stats);
diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h
index 0acd21480845..28e39620fd59 100644
--- a/src/ipa/ipu3/algorithms/awb.h
+++ b/src/ipa/ipu3/algorithms/awb.h
@@ -40,7 +40,7 @@ public:
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.cpp b/src/ipa/ipu3/algorithms/tone_mapping.cpp
index f86e79b24a67..977741c5a4d4 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.cpp
+++ b/src/ipa/ipu3/algorithms/tone_mapping.cpp
@@ -78,7 +78,8 @@ void ToneMapping::prepare([[maybe_unused]] IPAContext &context,
  * The tone mapping look up table is generated as an inverse power curve from
  * our gamma setting.
  */
-void ToneMapping::process(IPAContext &context, [[maybe_unused]] IPAFrameContext *frameContext,
+void ToneMapping::process(IPAContext &context,
+			  [[maybe_unused]] IPU3FrameContext &frameContext,
 			  [[maybe_unused]] const ipu3_uapi_stats_3a *stats)
 {
 	/*
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.h b/src/ipa/ipu3/algorithms/tone_mapping.h
index d7d4800628f2..8cce38c742a6 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.h
+++ b/src/ipa/ipu3/algorithms/tone_mapping.h
@@ -20,7 +20,7 @@ public:
 
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context, ipu3_uapi_params *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, IPU3FrameContext &frameContext,
 		     const ipu3_uapi_stats_3a *stats) override;
 
 private:
diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
index dd139cb4c868..536d76ecd63b 100644
--- a/src/ipa/ipu3/ipa_context.cpp
+++ b/src/ipa/ipu3/ipa_context.cpp
@@ -35,22 +35,6 @@ namespace libcamera::ipa::ipu3 {
  * most recently computed by the IPA algorithms.
  */
 
-/**
- * \struct IPAFrameContext
- * \brief Context for a frame
- *
- * The frame context stores data specific to a single frame processed by the
- * IPA. Each frame processed by the IPA has a context associated with it,
- * accessible through the IPAContext structure.
- *
- * Fields in the frame context should reflect values and controls
- * associated with the specific frame as requested by the application, and
- * as configured by the hardware. Fields can be read by algorithms to
- * determine if they should update any specific action for this frame, and
- * finally to update the metadata control lists when the frame is fully
- * completed.
- */
-
 /**
  * \struct IPAContext
  * \brief Global IPA context data shared between all algorithms
@@ -181,16 +165,16 @@ namespace libcamera::ipa::ipu3 {
  */
 
 /**
- * \var IPAFrameContext::frame
- * \brief The frame number
+ * \struct IPU3FrameContext
+ * \copybrief libcamera::ipa::IPAFrameContext
  *
- * \var IPAFrameContext::sensor
+ * \var IPU3FrameContext::sensor
  * \brief Effective sensor values that were applied for the frame
  *
- * \var IPAFrameContext::sensor.exposure
+ * \var IPU3FrameContext::sensor.exposure
  * \brief Exposure time expressed as a number of lines
  *
- * \var IPAFrameContext::sensor.gain
+ * \var IPU3FrameContext::sensor.gain
  * \brief Analogue gain multiplier
  */
 
diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
index 193fc44a821a..c4aa4c3f4f6a 100644
--- a/src/ipa/ipu3/ipa_context.h
+++ b/src/ipa/ipu3/ipa_context.h
@@ -73,8 +73,7 @@ struct IPAActiveState {
 	} toneMapping;
 };
 
-struct IPAFrameContext {
-	uint32_t frame;
+struct IPU3FrameContext : public IPAFrameContext {
 
 	struct {
 		uint32_t exposure;
@@ -86,7 +85,7 @@ struct IPAContext {
 	IPASessionConfiguration configuration;
 	IPAActiveState activeState;
 
-	FCQueue<IPAFrameContext> frameContexts;
+	FCQueue<IPU3FrameContext> frameContexts;
 };
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 55e82cd404f4..489e51bc6cf7 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -570,7 +570,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 	const ipu3_uapi_stats_3a *stats =
 		reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data());
 
-	IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+	IPU3FrameContext &frameContext = context_.frameContexts.get(frame);
 
 	if (frameContext.frame != frame)
 		LOG(IPAIPU3, Warning) << "Frame " << frame << " does not match its frame context";
@@ -583,7 +583,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 	ControlList ctrls(controls::controls);
 
 	for (auto const &algo : algorithms_)
-		algo->process(context_, &frameContext, stats);
+		algo->process(context_, frameContext, stats);
 
 	setControls(frame);
 
@@ -619,7 +619,7 @@ void IPAIPU3::processStatsBuffer(const uint32_t frame,
 void IPAIPU3::queueRequest(const uint32_t frame, const ControlList &controls)
 {
 	/* \todo Start processing for 'frame' based on 'controls'. */
-	IPAFrameContext &frameContext = context_.frameContexts.initialise(frame);
+	IPU3FrameContext &frameContext = context_.frameContexts.initialise(frame);
 
 	/* \todo Implement queueRequest to each algorithm. */
 	(void)frameContext;
diff --git a/src/ipa/ipu3/module.h b/src/ipa/ipu3/module.h
index d94fc4594871..6d0d50f615d8 100644
--- a/src/ipa/ipu3/module.h
+++ b/src/ipa/ipu3/module.h
@@ -19,7 +19,7 @@ namespace libcamera {
 
 namespace ipa::ipu3 {
 
-using Module = ipa::Module<IPAContext, IPAFrameContext, IPAConfigInfo,
+using Module = ipa::Module<IPAContext, IPU3FrameContext, IPAConfigInfo,
 			   ipu3_uapi_params, ipu3_uapi_stats_3a>;
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h
index 2a8871d831a3..adbcf69adb4f 100644
--- a/src/ipa/libipa/algorithm.h
+++ b/src/ipa/libipa/algorithm.h
@@ -41,7 +41,7 @@ public:
 	}
 
 	virtual void process([[maybe_unused]] typename Module::Context &context,
-			     [[maybe_unused]] typename Module::FrameContext *frameContext,
+			     [[maybe_unused]] typename Module::FrameContext &frameContext,
 			     [[maybe_unused]] const typename Module::Stats *stats)
 	{
 	}
diff --git a/src/ipa/libipa/fc_queue.cpp b/src/ipa/libipa/fc_queue.cpp
index ecb47f287350..627c2fa3301e 100644
--- a/src/ipa/libipa/fc_queue.cpp
+++ b/src/ipa/libipa/fc_queue.cpp
@@ -55,6 +55,27 @@ namespace ipa {
  * the Request when it completes.
  */
 
+/**
+ * \struct IPAFrameContext
+ * \brief Context for a frame
+ *
+ * The frame context stores data specific to a single frame processed by the
+ * IPA. Each frame processed by the IPA has a context associated with it,
+ * accessible through the Frame Context Queue.
+ *
+ * Fields in the frame context should reflect values and controls associated
+ * with the specific frame as requested by the application, and as configured by
+ * the hardware. Fields can be read by algorithms to determine if they should
+ * update any specific action for this frame, and finally to update the metadata
+ * control lists when the frame is fully completed.
+ *
+ * \var IPAFrameContext::frame
+ * \brief The frame number
+ *
+ * \var IPAFrameContext::error
+ * \brief The error flags associated with the frame
+ */
+
 /**
  * \fn FCQueue::clear()
  * \brief Reinitialise all data on the queue
diff --git a/src/ipa/libipa/fc_queue.h b/src/ipa/libipa/fc_queue.h
index 82ce36811926..6c7b8bf11ab2 100644
--- a/src/ipa/libipa/fc_queue.h
+++ b/src/ipa/libipa/fc_queue.h
@@ -13,6 +13,7 @@
 #include <libcamera/base/log.h>
 
 #include <libcamera/controls.h>
+#include <libcamera/request.h>
 
 namespace libcamera {
 
@@ -27,6 +28,11 @@ namespace ipa {
  */
 static constexpr uint32_t kMaxFrameContexts = 16;
 
+struct IPAFrameContext {
+	unsigned int frame;
+	Request::ErrorFlags error;
+};
+
 template<typename FrameContext>
 class FCQueue : public std::array<FrameContext, kMaxFrameContexts>
 {
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 483e941fe427..40d27963ef68 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -281,7 +281,7 @@ double Agc::measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const
  * new exposure and gain for the scene.
  */
 void Agc::process(IPAContext &context,
-		  [[maybe_unused]] IPAFrameContext *frameContext,
+		  [[maybe_unused]] RKISP1FrameContext &frameContext,
 		  const rkisp1_stat_buffer *stats)
 {
 	const rkisp1_cif_isp_stat *params = &stats->params;
diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
index 22c02779d311..1a291bdf3636 100644
--- a/src/ipa/rkisp1/algorithms/agc.h
+++ b/src/ipa/rkisp1/algorithms/agc.h
@@ -29,7 +29,7 @@ public:
 
 	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
 	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameContext,
+	void process(IPAContext &context, RKISP1FrameContext &frameContext,
 		     const rkisp1_stat_buffer *stats) override;
 
 private:
diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp
index 427aaeb1e955..3beafb73ca33 100644
--- a/src/ipa/rkisp1/algorithms/awb.cpp
+++ b/src/ipa/rkisp1/algorithms/awb.cpp
@@ -120,7 +120,7 @@ void Awb::prepare(IPAContext &context, rkisp1_params_cfg *params)
  * \copydoc libcamera::ipa::Algorithm::process
  */
 void Awb::process([[maybe_unused]] IPAContext &context,
-		  [[maybe_unused]] IPAFrameContext *frameCtx,
+		  [[maybe_unused]] RKISP1FrameContext &frameCtx,
 		  const rkisp1_stat_buffer *stats)
 {
 	const rkisp1_cif_isp_stat *params = &stats->params;
diff --git a/src/ipa/rkisp1/algorithms/awb.h b/src/ipa/rkisp1/algorithms/awb.h
index 7647842f6609..259b85eebdb4 100644
--- a/src/ipa/rkisp1/algorithms/awb.h
+++ b/src/ipa/rkisp1/algorithms/awb.h
@@ -23,7 +23,7 @@ public:
 
 	int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
 	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
-	void process(IPAContext &context, IPAFrameContext *frameCtx,
+	void process(IPAContext &context, RKISP1FrameContext &frameCtx,
 		     const rkisp1_stat_buffer *stats) override;
 
 private:
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index 341fef2e68fe..a64dbc75fdd2 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -14,6 +14,8 @@
 
 #include <libcamera/geometry.h>
 
+#include <libipa/fc_queue.h>
+
 namespace libcamera {
 
 namespace ipa::rkisp1 {
@@ -64,7 +66,7 @@ struct IPAActiveState {
 	unsigned int frameCount;
 };
 
-struct IPAFrameContext {
+struct RKISP1FrameContext : public IPAFrameContext {
 };
 
 struct IPAContext {
diff --git a/src/ipa/rkisp1/module.h b/src/ipa/rkisp1/module.h
index 89f83208a75c..2568c624df0f 100644
--- a/src/ipa/rkisp1/module.h
+++ b/src/ipa/rkisp1/module.h
@@ -19,7 +19,7 @@ namespace libcamera {
 
 namespace ipa::rkisp1 {
 
-using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,
+using Module = ipa::Module<IPAContext, RKISP1FrameContext, IPACameraSensorInfo,
 			   rkisp1_params_cfg, rkisp1_stat_buffer>;
 
 } /* namespace ipa::rkisp1 */
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index 9629ccbf4247..7481e67e70f6 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -301,8 +301,11 @@ void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId
 
 	unsigned int aeState = 0;
 
+	/* \todo Obtain the frame context to pass to process from the FCQueue */
+	RKISP1FrameContext frameContext;
+
 	for (auto const &algo : algorithms())
-		algo->process(context_, nullptr, stats);
+		algo->process(context_, frameContext, stats);
 
 	setControls(frame);
 
