diff --git a/bin/sozo/src/commands/auth.rs b/bin/sozo/src/commands/auth.rs
index 550ece1124..67ab7a5016 100644
--- a/bin/sozo/src/commands/auth.rs
+++ b/bin/sozo/src/commands/auth.rs
@@ -64,7 +64,7 @@ impl AuthArgs {
         trace!(metadata=?env_metadata, "Loaded environment.");
 
         let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
-        let default_namespace = get_default_namespace_from_ws(&ws);
+        let default_namespace = get_default_namespace_from_ws(&ws)?;
 
         match self.command {
             AuthCommand::Grant { kind, world, starknet, account, transaction } => {
diff --git a/bin/sozo/src/commands/build.rs b/bin/sozo/src/commands/build.rs
index da94807c98..8a21ef14dd 100644
--- a/bin/sozo/src/commands/build.rs
+++ b/bin/sozo/src/commands/build.rs
@@ -45,6 +45,23 @@ impl BuildArgs {
     pub fn run(self, config: &Config) -> Result<()> {
         let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
 
+        if let Ok(current_package) = ws.current_package() {
+            if current_package.target(&TargetKind::new("dojo")).is_none() {
+                return Err(anyhow::anyhow!(
+                    "No Dojo target found in the {} package. Add [[target.dojo]] to the {} \
+                     manifest to enable Dojo features and compile with sozo.",
+                    current_package.id.to_string(),
+                    current_package.manifest_path()
+                ));
+            }
+        }
+
+        // Namespaces are required to compute contracts/models data. Hence, we can't continue
+        // if no metadata are found.
+        // Once sozo will support package option, users will be able to do `-p` to select
+        // the package directly from the workspace instead of using `--manifest-path`.
+        let dojo_metadata = dojo_metadata_from_workspace(&ws)?;
+
         let profile_name =
             ws.current_profile().expect("Scarb profile is expected at this point.").to_string();
 
@@ -126,13 +143,10 @@ impl BuildArgs {
         };
         trace!(pluginManager=?bindgen, "Generating bindings.");
 
-        // Only generate bindgen if a current package is defined with dojo metadata.
-        if let Ok(dojo_metadata) = dojo_metadata_from_workspace(&ws) {
-            tokio::runtime::Runtime::new()
-                .unwrap()
-                .block_on(bindgen.generate(dojo_metadata.skip_migration))
-                .expect("Error generating bindings");
-        };
+        tokio::runtime::Runtime::new()
+            .unwrap()
+            .block_on(bindgen.generate(dojo_metadata.skip_migration))
+            .expect("Error generating bindings");
 
         Ok(())
     }
diff --git a/bin/sozo/src/commands/call.rs b/bin/sozo/src/commands/call.rs
index fcac0ac293..beb2497809 100644
--- a/bin/sozo/src/commands/call.rs
+++ b/bin/sozo/src/commands/call.rs
@@ -47,7 +47,7 @@ impl CallArgs {
             self.tag_or_address
         } else {
             let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
-            let default_namespace = get_default_namespace_from_ws(&ws);
+            let default_namespace = get_default_namespace_from_ws(&ws)?;
             ensure_namespace(&self.tag_or_address, &default_namespace)
         };
 
diff --git a/bin/sozo/src/commands/execute.rs b/bin/sozo/src/commands/execute.rs
index a100285df0..9d2dcf4faa 100644
--- a/bin/sozo/src/commands/execute.rs
+++ b/bin/sozo/src/commands/execute.rs
@@ -56,7 +56,7 @@ impl ExecuteArgs {
             self.tag_or_address
         } else {
             let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
-            let default_namespace = get_default_namespace_from_ws(&ws);
+            let default_namespace = get_default_namespace_from_ws(&ws)?;
             ensure_namespace(&self.tag_or_address, &default_namespace)
         };
 
diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs
index c0e1997582..5ad4d5412e 100644
--- a/bin/sozo/src/commands/model.rs
+++ b/bin/sozo/src/commands/model.rs
@@ -113,7 +113,7 @@ impl ModelArgs {
         trace!(args = ?self);
         let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
         let env_metadata = utils::load_metadata_from_config(config)?;
-        let default_namespace = get_default_namespace_from_ws(&ws);
+        let default_namespace = get_default_namespace_from_ws(&ws)?;
 
         config.tokio_handle().block_on(async {
             match self.command {
diff --git a/bin/sozo/tests/register_test.rs b/bin/sozo/tests/register_test.rs
index f9dd35f063..560ae8f103 100644
--- a/bin/sozo/tests/register_test.rs
+++ b/bin/sozo/tests/register_test.rs
@@ -28,7 +28,7 @@ async fn reregister_models() {
     let target_path =
         ws.target_dir().path_existent().unwrap().join(ws.config().profile().to_string());
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let migration = prepare_migration(
         source_project_dir.clone(),
diff --git a/crates/dojo-lang/src/compiler.rs b/crates/dojo-lang/src/compiler.rs
index 97eeff00b9..826d1dc4fc 100644
--- a/crates/dojo-lang/src/compiler.rs
+++ b/crates/dojo-lang/src/compiler.rs
@@ -87,7 +87,7 @@ impl Compiler for DojoCompiler {
         let props: Props = unit.main_component().target_props()?;
         let target_dir = unit.target_dir(ws);
 
-        let default_namespace = get_default_namespace_from_ws(ws);
+        let default_namespace = get_default_namespace_from_ws(ws)?;
 
         let compiler_config = build_compiler_config(&unit, ws);
 
diff --git a/crates/dojo-world/src/metadata.rs b/crates/dojo-world/src/metadata.rs
index 31774c5a3e..b796ae4636 100644
--- a/crates/dojo-world/src/metadata.rs
+++ b/crates/dojo-world/src/metadata.rs
@@ -53,10 +53,9 @@ fn build_artifact_from_filename(
 /// # Returns
 ///
 /// A [`String`] object containing the namespace.
-pub fn get_default_namespace_from_ws(ws: &Workspace<'_>) -> String {
-    let metadata = dojo_metadata_from_workspace(ws)
-        .expect("Namespace key is already checked by the parsing of the Scarb.toml file.");
-    metadata.world.namespace
+pub fn get_default_namespace_from_ws(ws: &Workspace<'_>) -> Result<String> {
+    let metadata = dojo_metadata_from_workspace(ws)?;
+    Ok(metadata.world.namespace)
 }
 
 /// Build world metadata with data read from the project configuration.
@@ -99,13 +98,18 @@ pub fn dojo_metadata_from_workspace(ws: &Workspace<'_>) -> Result<DojoMetadata>
     let source_dir = source_dir.join(profile.as_str());
 
     let project_metadata = if let Ok(current_package) = ws.current_package() {
-        current_package.manifest.metadata.dojo()?
+        current_package
+            .manifest
+            .metadata
+            .dojo()
+            .with_context(|| format!("Error parsing manifest file `{}`", ws.manifest_path()))?
     } else {
         // On workspaces, dojo metadata are not accessible because if no current package is defined
         // (being the only package or using --package).
         return Err(anyhow!(
-            "No current package with dojo metadata found, this subcommand is not yet support for \
-             workspaces."
+            "No current package with dojo metadata found, virtual manifest in workspace are not \
+             supported. Until package compilation is supported, you will have to provide the path \
+             to the Scarb.toml file using the --manifest-path option."
         ));
     };
 
@@ -438,13 +442,14 @@ impl MetadataExt for ManifestMetadata {
             .tool_metadata
             .as_ref()
             .and_then(|e| e.get("dojo"))
-            // TODO: see if we can make error more descriptive
-            .ok_or_else(|| anyhow!("Some of the fields in [tool.dojo] are required."))?
+            .with_context(|| "No [tool.dojo] section found in the manifest.".to_string())?
             .clone();
 
+        // The details of which field has failed to be loaded are logged inside the `try_into`
+        // error.
         let project_metadata: ProjectMetadata = metadata
             .try_into()
-            .with_context(|| "Project metadata (i.e. [tool.dojo]) is not properly configured.")?;
+            .with_context(|| "Project metadata [tool.dojo] is not properly configured.")?;
 
         Ok(project_metadata)
     }
diff --git a/crates/sozo/ops/src/migration/mod.rs b/crates/sozo/ops/src/migration/mod.rs
index 281bffc1f4..fda02e8325 100644
--- a/crates/sozo/ops/src/migration/mod.rs
+++ b/crates/sozo/ops/src/migration/mod.rs
@@ -78,7 +78,7 @@ where
     let target_dir = ws.target_dir().path_existent().unwrap();
     let target_dir = target_dir.join(ws.config().profile().as_str());
 
-    let default_namespace = get_default_namespace_from_ws(ws);
+    let default_namespace = get_default_namespace_from_ws(ws)?;
 
     // Load local and remote World manifests.
     let (local_manifest, remote_manifest) = utils::load_world_manifests(
diff --git a/crates/sozo/ops/src/tests/migration.rs b/crates/sozo/ops/src/tests/migration.rs
index 50f07a04b3..81847f2bca 100644
--- a/crates/sozo/ops/src/tests/migration.rs
+++ b/crates/sozo/ops/src/tests/migration.rs
@@ -205,7 +205,7 @@ async fn migration_with_correct_calldata_second_time_work_as_expected() {
         // adding correct calldata
         manifest.merge(overlay);
     }
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let mut world = WorldDiff::compute(manifest, Some(remote_manifest));
     world.update_order(&default_namespace).expect("Failed to update order");
@@ -375,7 +375,7 @@ async fn migrate_with_auto_authorize() {
     let world_address = migration.world_address().expect("must be present");
     let world = WorldContract::new(world_address, account);
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
     let res =
         auto_authorize(&ws, &world, &txn_config, &manifest, &output, &default_namespace).await;
     assert!(res.is_ok());
@@ -408,7 +408,7 @@ async fn migration_with_mismatching_world_address_and_seed() {
     let base_dir = config.manifest_path().parent().unwrap().to_path_buf();
     let target_dir = base_dir.join("target").join("dev");
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let strategy = prepare_migration_with_world_and_seed(
         base_dir,
diff --git a/crates/sozo/ops/src/tests/setup.rs b/crates/sozo/ops/src/tests/setup.rs
index 4ddb0f1459..664ff5e858 100644
--- a/crates/sozo/ops/src/tests/setup.rs
+++ b/crates/sozo/ops/src/tests/setup.rs
@@ -61,7 +61,7 @@ pub fn setup_migration(config: &Config) -> Result<MigrationStrategy> {
     let base_dir = manifest_path.parent().unwrap();
     let target_dir = format!("{}/target/dev", base_dir);
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     prepare_migration_with_world_and_seed(
         base_dir.into(),
diff --git a/crates/torii/core/src/sql_test.rs b/crates/torii/core/src/sql_test.rs
index 8477470734..b367eeab61 100644
--- a/crates/torii/core/src/sql_test.rs
+++ b/crates/torii/core/src/sql_test.rs
@@ -77,7 +77,7 @@ async fn test_load_from_remote() {
     let base_dir = manifest_path.parent().unwrap();
     let target_dir = format!("{}/target/dev", base_dir);
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let mut migration = prepare_migration(
         base_dir.into(),
@@ -222,7 +222,7 @@ async fn test_load_from_remote_del() {
     let base_dir = manifest_path.parent().unwrap();
     let target_dir = format!("{}/target/dev", base_dir);
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let mut migration = prepare_migration(
         base_dir.into(),
diff --git a/crates/torii/graphql/src/tests/mod.rs b/crates/torii/graphql/src/tests/mod.rs
index f9b187c1e8..8bfba50696 100644
--- a/crates/torii/graphql/src/tests/mod.rs
+++ b/crates/torii/graphql/src/tests/mod.rs
@@ -291,7 +291,7 @@ pub async fn spinup_types_test() -> Result<SqlitePool> {
 
     let target_path = ws.target_dir().path_existent().unwrap().join(config.profile().to_string());
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let mut migration = prepare_migration(
         source_project_dir,
diff --git a/crates/torii/grpc/src/server/tests/entities_test.rs b/crates/torii/grpc/src/server/tests/entities_test.rs
index 8aeb9a0756..205fda416e 100644
--- a/crates/torii/grpc/src/server/tests/entities_test.rs
+++ b/crates/torii/grpc/src/server/tests/entities_test.rs
@@ -50,7 +50,7 @@ async fn test_entities_queries() {
 
     let target_path = ws.target_dir().path_existent().unwrap().join(config.profile().to_string());
 
-    let default_namespace = get_default_namespace_from_ws(&ws);
+    let default_namespace = get_default_namespace_from_ws(&ws).unwrap();
 
     let mut migration = prepare_migration(
         config.manifest_path().parent().unwrap().into(),