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
While translating a deno.json into package.json metadata for packing, the exports rewriter only accepts a string or a plain object. Any other JSON type (array, number, boolean, null) falls through to this bail with the offending value printed.
Solutions
- Make `exports` a string (`"exports": "./mod.ts"`) or a conditions object (`{"exports": {".": "./mod.ts"}}`).
- If you copied from package.json, convert array forms to their first entry or an object mapping.
- Validate the config with `deno publish --dry-run` or a JSON schema before packing.
Example fix
// deno.json (before)
{
"exports": ["./mod.ts", "./lib.ts"]
}
// deno.json (after)
{
"exports": { ".": "./mod.ts", "./lib": "./lib.ts" }
} Defensive patterns
Strategy: type-guard
Validate before calling
const cfg = JSON.parse(readFileSync("deno.json", "utf8"));
const e = cfg.exports;
if (typeof e !== "string" && (typeof e !== "object" || e === null || Array.isArray(e))) {
console.error("deno.json 'exports' must be a string or an object");
process.exit(1);
} Type guard
const isValidExports = (e: unknown): e is string | Record<string, unknown> => typeof e === "string" || (typeof e === "object" && e !== null && !Array.isArray(e));
Prevention
- Never copy npm's array form of `exports` into deno.json.
- Use the deno.json JSON schema in editors to get type hints for `exports`.
- Run `deno publish --dry-run` after editing exports.
When it happens
Trigger: deno.json `"exports"` set to an array like `["./mod.ts"]`, a boolean, a number, or otherwise non-string/non-object JSON — including malformed nesting from hand edits.
Common situations: Copying an npm package.json `exports` array shape (npm allows arrays, deno.json does not);. Editing deno.json by hand and leaving a trailing value or wrong-type placeholder. Tooling that serializes exports from a list without converting to an object.
Related errors
- Missing 'exports' field in
- Missing name in config
- Missing name
- Missing 'name' field in
- Missing 'version' field in
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e4483ca46305a39a.
Report an issue: GitHub.
Appendix: 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 9ad36f7a2c)