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

  1. Make `exports` a string (`"exports": "./mod.ts"`) or a conditions object (`{"exports": {".": "./mod.ts"}}`).
  2. If you copied from package.json, convert array forms to their first entry or an object mapping.
  3. 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

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


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)