PaddlePaddle/PaddleOCR · error · Error

Asset "${assetName}" must define url.

Error message

Asset "${assetName}" must define url.

What it means

Thrown by normalizeModelAsset() when the asset entry is an object but its url field is missing, empty, or not a non-empty string. The object form of an asset exists solely to carry a URL to a tar model bundle, so the url must be a non-empty string. This fires before any fetch, catching malformed config early.

Source

Thrown at paddleocr-js/packages/core/src/resources/model-asset.ts:73

function isObject(value: unknown): value is Record<string, unknown> {
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
}

export function normalizeModelAsset(assetName: string, asset: unknown): ModelAsset {
  if (isNonEmptyString(asset)) {
    const resolvedAsset = DEFAULT_MODEL_ASSETS[asset];
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for missing Record key
    if (!resolvedAsset) {
      throw new Error(`Asset "${assetName}" references unknown model asset "${asset}".`);
    }
    return { url: resolvedAsset.url };
  }

  if (!isObject(asset)) {
    throw new Error(`Asset "${assetName}" must be an object.`);
  }
  if (!isNonEmptyString(asset.url)) {
    throw new Error(`Asset "${assetName}" must define url.`);
  }

  return {
    url: asset.url
  };
}

export function normalizeAssets(
  assets: Record<string, unknown> | undefined
): Record<string, ModelAsset> {
  const assetEntries = Object.entries(assets || {});

  if (assetEntries.length === 0) {
    throw new Error("Assets must define at least one asset.");
  }

  return Object.fromEntries(
    assetEntries.map(([assetName, asset]) => [assetName, normalizeModelAsset(assetName, asset)])

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Set a non-empty url string: assets: { det: { url: "https://..." } }
  2. Check for typos in the key name (must be exactly url)
  3. Assert the URL variable is a non-empty string before building the config

Example fix

// before
const ocr = await PaddleOCR.create({ assets: { det: { url: process.env.DET_URL } } }); // env unset -> throws

// after
if (!process.env.DET_URL) throw new Error("DET_URL is required");
const ocr = await PaddleOCR.create({ assets: { det: { url: process.env.DET_URL } } });
Defensive patterns

Strategy: validation

Validate before calling

function hasNonEmptyUrl(asset: unknown): boolean {
  return (
    typeof asset === "object" && asset !== null &&
    typeof (asset as Record<string, unknown>).url === "string" &&
    ((asset as Record<string, unknown>).url as string).length > 0
  );
}

Type guard

function isUrlAsset(v: unknown): v is { url: string } {
  return typeof (v as { url?: unknown })?.url === "string" && (v as { url: string }).url.length > 0;
}

Prevention

When it happens

Trigger: Passing { det: {} }, { det: { url: "" } }, or { det: { url: undefined } }; building the URL from a variable that is undefined at runtime; trailing-comma or key-typo objects like { uri: "..." }.

Common situations: Template-literal URLs where an env var is unset producing "undefined" or empty string; key renamed during a refactor (src -> url); config merging that drops the url field.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/1fce072ad13264f6. Report an issue: GitHub.