PaddlePaddle/PaddleOCR · error · Error

Asset "${assetName}" must be an object.

Error message

Asset "${assetName}" must be an object.

What it means

Thrown by normalizeModelAsset() when the asset value is neither a non-empty string (built-in name) nor a plain object - e.g. a number, boolean, null, or array. Each asset entry must be a preset name string or an object with a url field; anything else is rejected at normalization time before any network activity.

Source

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

  return typeof value === "string" && value.length > 0;
}

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.");
  }

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Set each asset entry to a string preset name or { url: "..." }
  2. Validate the config shape before calling create() (see type guard)
  3. If the value comes from JSON, add a schema check at load time

Example fix

// before
const ocr = await PaddleOCR.create({ assets: { det: null } });

// after
const ocr = await PaddleOCR.create({ assets: { det: { url: "https://host/det.tar" } } });
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

function isAssetShape(v: unknown): v is string | { url: string } {
  if (typeof v === "string") return v.length > 0;
  return isPlainObject(v) && typeof (v as any).url === "string" && (v as any).url.length > 0;
}

Prevention

When it happens

Trigger: Passing assets: { det: 42 }, { det: null }, { det: ["https://..."] }, or spreading undefined over a default entry; JSON configs where the asset got serialized incorrectly.

Common situations: Config built from environment variables without casting ("" or numbers); YAML/JSON pipeline where the asset key is present but the value is a list or null; accidental nesting like { det: { model: { url } } }.

Related errors


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