denoland/deno · error

unsupported 'exports' shape in deno.json: expected a string

Error message

unsupported 'exports' shape in deno.json: expected a string or object, got {}

What it means

`deno pack` converts deno.json's "exports" field into package.json exports. Only two shapes are accepted: a plain string ("./mod.ts") or an object mapping keys/conditions to values. Any other JSON type (array, number, boolean) is rejected with this message showing the offending value.

Source

Thrown at cli/tools/pack/package_json.rs:180

              // omit `types` when no .d.ts was generated
            } else {
              rewritten.insert(
                condition.clone(),
                json!(format!("./{}", ts_to_js_extension(path))),
              );
            }
          } else {
            rewritten.insert(condition.clone(), cond_value.clone());
          }
        }
        result.insert(key.clone(), serde_json::Value::Object(rewritten));
      }
    }

    return Ok(serde_json::Value::Object(result));
  }

  deno_core::anyhow::bail!(
    "unsupported 'exports' shape in deno.json: expected a string or object, got {}",
    exports
  )
}

fn extract_main_and_types(
  exports: &Option<serde_json::Value>,
  dts_set: &HashSet<String>,
) -> (Option<String>, Option<String>) {
  let Some(exports) = exports else {
    let types = if dts_set.contains("mod.js") {
      Some("./mod.d.ts".to_string())
    } else {
      None
    };
    return (Some("./mod.js".to_string()), types);
  };

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Change "exports" to a string or object form
  2. For multiple entries use the object form keyed by subpath (".", "./sub", ...)
  3. Validate the config shape before packing (see type guard below)

Example fix

// deno.json before
"exports": ["./mod.ts"]
// after
"exports": "./mod.ts"
// or
"exports": { ".": "./mod.ts", "./sub": "./sub.ts" }
Defensive patterns

Strategy: type-guard

Validate before calling

deno eval 'const e = JSON.parse(Deno.readTextSync("deno.json")).exports; if (e !== undefined && typeof e !== "string" && (typeof e !== "object" || Array.isArray(e))) throw new Error("exports must be a string or object");'

Type guard

function isValidExports(v: unknown): boolean {
  return typeof v === "string"
    || (typeof v === "object" && v !== null && !Array.isArray(v));
}

Prevention

When it happens

Trigger: A deno.json like { "exports": ["./mod.ts"] } or { "exports": 42 } passed to `deno pack`.

Common situations: Copy-pasting array-style exports from Node package.json docs; typos wrapping exports in brackets; code generators emitting arrays.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/e4483ca46305a39a. Report an issue: GitHub.