diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs
index 2026f97b..73d8b6a4 100644
--- a/juniper/src/executor/mod.rs
+++ b/juniper/src/executor/mod.rs
@@ -85,6 +85,12 @@ where
     field_path: Arc<FieldPath<'a>>,
 }
 
+impl<'r, 'a, CX: ?Sized, SV> Executor<'r, 'a, CX, SV> {
+    pub(crate) fn current_type_reworked(&self) -> &TypeType<'a, SV> {
+        &self.current_type
+    }
+}
+
 /// Error type for errors that occur during query execution
 ///
 /// All execution errors contain the source position in the query of the field
@@ -1381,17 +1387,17 @@ impl<'r, S: 'r> Registry<'r, S> {
     /// values of this type matches it.
     ///
     /// [`graphql::Type`]: resolve::Type
-    pub fn build_list_type_new<T, Info>(
+    pub fn wrap_list<'ti, T, TI>(
         &mut self,
-        info: &Info,
+        type_info: &'ti TI,
         expected_size: Option<usize>,
-    ) -> ListMeta<'r>
+    ) -> MetaType<'r, S>
     where
-        T: resolve::Type<Info, S> + ?Sized,
-        Info: ?Sized,
+        T: resolve::Type<TI, S> + ?Sized,
+        TI: ?Sized,
+        'ti: 'r,
     {
-        todo!()
-        //ListMeta::new(T::meta(self, info).as_type(), expected_size)
+        ListMeta::new(T::meta(self, type_info).into(), expected_size).into_meta()
     }
 
     /// Creates a [`NullableMeta`] type.
diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs
index 1113cffe..47e7f705 100644
--- a/juniper/src/schema/meta.rs
+++ b/juniper/src/schema/meta.rs
@@ -563,7 +563,7 @@ impl<'a, S> ScalarMeta<'a, S> {
 }
 
 impl<'a> ListMeta<'a> {
-    /// Build a new [`ListMeta`] type by wrapping the specified [`Type`].
+    /// Builds a new [`ListMeta`] type by wrapping the specified [`Type`].
     ///
     /// Specifying `expected_size` will be used to ensure that values of this
     /// type will always match it.
@@ -581,7 +581,7 @@ impl<'a> ListMeta<'a> {
 }
 
 impl<'a> NullableMeta<'a> {
-    /// Build a new [`NullableMeta`] type by wrapping the specified [`Type`].
+    /// Builds a new [`NullableMeta`] type by wrapping the specified [`Type`].
     pub fn new(of_type: Type<'a>) -> Self {
         Self { of_type }
     }
diff --git a/juniper/src/types/iter.rs b/juniper/src/types/iter.rs
index 8d6aad9d..f0f599a0 100644
--- a/juniper/src/types/iter.rs
+++ b/juniper/src/types/iter.rs
@@ -2,28 +2,28 @@
 
 use crate::{graphql, resolve, ExecutionResult, Executor, Selection};
 
-/*
-pub fn resolve_list<'t, T, S, Info, Ctx, I>(
+pub fn resolve_list<'t, T, TI, CX, SV, BH, I>(
     iter: I,
-    selection_set: Option<&[Selection<'_, S>]>,
-    info: &Info,
-    executor: &Executor<Ctx, S>,
-) -> ExecutionResult<S>
+    selection_set: Option<&[Selection<'_, SV>]>,
+    type_info: &TI,
+    executor: &Executor<CX, SV>,
+) -> ExecutionResult<SV>
 where
     I: Iterator<Item = &'t T> + ExactSizeIterator,
-    T: resolve::Value<Info, Ctx, S> + ?Sized + 't,
-    Info: ?Sized,
-    Ctx: ?Sized,
+    T: resolve::Value<TI, CX, SV, BH> + ?Sized + 't,
+    TI: ?Sized,
+    CX: ?Sized,
+    BH: ?Sized,
 {
     let is_non_null = executor
-        .current_type_new()
+        .current_type_reworked()
         .list_contents()
         .ok_or("Iterating over non-list type")?
         .is_non_null();
 
     let mut values = Vec::with_capacity(iter.len());
     for v in iter {
-        let val = v.resolve_value(selection_set, info, executor)?;
+        let val = v.resolve_value(selection_set, type_info, executor)?;
         if is_non_null && val.is_null() {
             return Err("Resolved `null` on non-null type".into());
         }
@@ -32,28 +32,32 @@ where
     Ok(graphql::Value::list(values))
 }
 
-pub async fn resolve_list_async<'a, 't, T, S, Info, Ctx, I>(
+pub async fn resolve_list_async<'t, 'r, T, TI, CX, SV, BH, I>(
     iter: I,
-    selection_set: Option<&[Selection<'_, S>]>,
-    info: &'a Info,
-    executor: &'a Executor<'a, 'a, Ctx, S>,
-) -> ExecutionResult<S>
+    selection_set: Option<&[Selection<'_, SV>]>,
+    type_info: &'r TI,
+    executor: &'r Executor<'r, '_, CX, SV>,
+) -> ExecutionResult<SV>
 where
     I: Iterator<Item = &'t T> + ExactSizeIterator,
-    T: resolve::ValueAsync<Info, Ctx, S> + ?Sized + 't,
-    Info: ?Sized,
-    Ctx: ?Sized,
+    T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized + 't,
+    TI: ?Sized,
+    CX: ?Sized,
+    BH: ?Sized,
 {
     use futures::stream::{FuturesOrdered, StreamExt as _};
 
     let is_non_null = executor
-        .current_type_new()
+        .current_type_reworked()
         .list_contents()
         .ok_or("Iterating over non-list type")?
         .is_non_null();
 
     let mut futs = iter
-        .map(|v| async move { v.resolve_value_async(selection_set, info, executor).await })
+        .map(|v| async move {
+            v.resolve_value_async(selection_set, type_info, executor)
+                .await
+        })
         .collect::<FuturesOrdered<_>>();
 
     let mut values = Vec::with_capacity(futs.len());
@@ -66,4 +70,3 @@ where
     }
     Ok(graphql::Value::list(values))
 }
-*/
diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs
index d331959a..44635ccd 100644
--- a/juniper/src/types/mod.rs
+++ b/juniper/src/types/mod.rs
@@ -10,7 +10,7 @@ mod ref_mut;
 mod result;
 mod slice;
 mod r#str;
-mod vec;
+pub mod vec;
 
 pub mod async_await;
 pub mod base;
diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs
index ad4cd8ed..df2d87a6 100644
--- a/juniper/src/types/vec.rs
+++ b/juniper/src/types/vec.rs
@@ -1,114 +1,175 @@
 //! GraphQL implementation for [`Vec`].
 
 use crate::{
+    behavior,
     executor::{ExecutionResult, Executor, Registry},
     graphql, reflect, resolve,
     schema::meta::MetaType,
-    BoxFuture, Selection,
+    BoxFuture, FieldError, IntoFieldError, Selection,
 };
 
 use super::iter;
 
-/*
-impl<T, Info, S> resolve::Type<Info, S> for Vec<T>
+impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Vec<T>
 where
-    T: resolve::Type<Info, S>,
-    Info: ?Sized,
+    T: resolve::Type<TI, SV, BH>,
+    TI: ?Sized,
+    BH: ?Sized,
 {
-    fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S>
+    fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV>
     where
-        S: 'r,
+        SV: 'r,
     {
-        registry.build_list_type_new::<T, _>(info, None).into_meta()
+        registry.wrap_list::<behavior::Coerce<T, BH>, _>(type_info, None)
     }
 }
 
-impl<T, Info, Ctx, S> resolve::Value<Info, Ctx, S> for Vec<T>
+impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Vec<T>
 where
-    T: resolve::Value<Info, Ctx, S>,
-    Info: ?Sized,
-    Ctx: ?Sized,
+    T: resolve::Value<TI, CX, SV, BH>,
+    TI: ?Sized,
+    CX: ?Sized,
+    BH: ?Sized,
 {
     fn resolve_value(
         &self,
-        selection_set: Option<&[Selection<'_, S>]>,
-        info: &Info,
-        executor: &Executor<Ctx, S>,
-    ) -> ExecutionResult<S> {
-        iter::resolve_list(self.iter(), selection_set, info, executor)
+        selection_set: Option<&[Selection<'_, SV>]>,
+        type_info: &TI,
+        executor: &Executor<CX, SV>,
+    ) -> ExecutionResult<SV> {
+        iter::resolve_list(self.iter(), selection_set, type_info, executor)
     }
 }
 
-impl<T, Info, Ctx, S> resolve::ValueAsync<Info, Ctx, S> for Vec<T>
+impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Vec<T>
 where
-    T: resolve::ValueAsync<Info, Ctx, S> + Sync,
-    Info: Sync + ?Sized,
-    Ctx: Sync + ?Sized,
-    S: Send + Sync,
+    T: resolve::ValueAsync<TI, CX, SV, BH> + Sync,
+    TI: Sync + ?Sized,
+    CX: Sync + ?Sized,
+    SV: Send + Sync,
+    BH: ?Sized + 'static, // TODO: Lift `'static` bound if possible.
 {
     fn resolve_value_async<'r>(
         &'r self,
-        selection_set: Option<&'r [Selection<'_, S>]>,
-        info: &'r Info,
-        executor: &'r Executor<Ctx, S>,
-    ) -> BoxFuture<'r, ExecutionResult<S>> {
+        selection_set: Option<&'r [Selection<'_, SV>]>,
+        type_info: &'r TI,
+        executor: &'r Executor<CX, SV>,
+    ) -> BoxFuture<'r, ExecutionResult<SV>> {
         Box::pin(iter::resolve_list_async(
             self.iter(),
             selection_set,
-            info,
+            type_info,
             executor,
         ))
     }
 }
 
-impl<T, S> resolve::ToInputValue<S> for Vec<T>
+impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Vec<T>
 where
-    T: resolve::ToInputValue<S>,
+    T: resolve::ToInputValue<SV, BH>,
+    BH: ?Sized,
 {
-    fn to_input_value(&self) -> graphql::InputValue<S> {
+    fn to_input_value(&self) -> graphql::InputValue<SV> {
         graphql::InputValue::list(self.iter().map(T::to_input_value))
     }
 }
 
-
-impl<'i, T, Info, S> graphql::InputType<'i, Info, S> for Vec<T>
+impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Vec<T>
 where
-    T: graphql::InputType<'i, Info, S>,
-    Info: ?Sized,
+    T: resolve::InputValue<'i, SV, BH>,
+    SV: 'i,
+    BH: ?Sized,
+{
+    type Error = TryFromInputValueError<T::Error>;
+
+    fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
+        match v {
+            graphql::InputValue::List(l) => l
+                .iter()
+                .map(|i| T::try_from_input_value(&i.item).map_err(TryFromInputValueError::Item))
+                .collect(),
+            // See "Input Coercion" on List types:
+            // https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
+            graphql::InputValue::Null => Err(TryFromInputValueError::IsNull),
+            other => T::try_from_input_value(other)
+                .map(|e| vec![e])
+                .map_err(TryFromInputValueError::Item),
+        }
+    }
+}
+
+impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Vec<T>
+where
+    T: graphql::InputType<'i, TI, SV, BH>,
+    TI: ?Sized,
+    SV: 'i,
+    BH: ?Sized,
 {
     fn assert_input_type() {
         T::assert_input_type()
     }
 }
 
-
-impl<T, S> graphql::OutputType<S> for Vec<T>
+impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Vec<T>
 where
-    T: graphql::OutputType<S>,
+    T: graphql::OutputType<TI, CX, SV, BH>,
+    TI: ?Sized,
+    CX: ?Sized,
+    BH: ?Sized,
+    Self: resolve::ValueAsync<TI, CX, SV, BH>,
 {
     fn assert_output_type() {
         T::assert_output_type()
     }
 }
 
-impl<T, S> reflect::BaseType<S> for Vec<T>
+impl<T, BH> reflect::BaseType<BH> for Vec<T>
 where
-    T: reflect::BaseType<S>,
+    T: reflect::BaseType<BH>,
+    BH: ?Sized,
 {
     const NAME: reflect::Type = T::NAME;
 }
 
-impl<T, S> reflect::BaseSubTypes<S> for Vec<T>
+impl<T, BH> reflect::BaseSubTypes<BH> for Vec<T>
 where
-    T: reflect::BaseSubTypes<S>,
+    T: reflect::BaseSubTypes<BH>,
+    BH: ?Sized,
 {
     const NAMES: reflect::Types = T::NAMES;
 }
 
-impl<T, S> reflect::WrappedType<S> for Vec<T>
+impl<T, BH> reflect::WrappedType<BH> for Vec<T>
 where
-    T: reflect::WrappedType<S>,
+    T: reflect::WrappedType<BH>,
+    BH: ?Sized,
 {
     const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE);
 }
-*/
+
+/// Possible errors of converting a [`graphql::InputValue`] into a [`Vec`].
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TryFromInputValueError<E> {
+    /// [`graphql::InputValue`] cannot be [`Null`].
+    ///
+    /// See ["Combining List and Non-Null" section of spec][0].
+    ///
+    /// [`Null`]: [`InputValue::Null`]
+    /// [0]: https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null
+    IsNull,
+
+    /// Error of converting a [`graphql::InputValue::List`]'s item.
+    Item(E),
+}
+
+impl<E, SV> IntoFieldError<SV> for TryFromInputValueError<E>
+where
+    E: IntoFieldError<SV>,
+{
+    fn into_field_error(self) -> FieldError<SV> {
+        match self {
+            Self::IsNull => "Failed to convert into `Vec`: Value cannot be `null`".into(),
+            Self::Item(e) => e.into_field_error(),
+        }
+    }
+}
diff --git a/juniper_codegen/src/common/mod.rs b/juniper_codegen/src/common/mod.rs
index ae2e9257..638042dd 100644
--- a/juniper_codegen/src/common/mod.rs
+++ b/juniper_codegen/src/common/mod.rs
@@ -1,7 +1,7 @@
 //! Common functions, definitions and extensions for code generation, used by this crate.
 
+pub(crate) mod behavior;
 pub(crate) mod field;
 pub(crate) mod gen;
 pub(crate) mod parse;
 pub(crate) mod scalar;
-pub(crate) mod behavior;