deepseek-ai/deepseek-harness · error

${name} must be an object

Error message

${name} must be an object

What it means

Every element of the modelPolicies array is validated by assertModelPolicy() before use. An element that is not a plain object — a string, number, boolean, null, or nested array — fails the '${name} must be an object' check, where name is 'BasicCompactionConfig: modelPolicies[<index>]' pointing at the exact bad element.

Source

Thrown at packages/compaction/compaction-basic/src/config.ts:219

    const name = `BasicCompactionConfig: modelPolicies[${index}]`
    assertModelPolicy(source, name)
    const key = `${source.provider}\u0000${source.model}`
    if (seen.has(key)) {
      throw new Error(
        `BasicCompactionConfig: duplicate model policy for ${source.provider}/${source.model}`,
      )
    }
    seen.add(key)
    return { ...source }
  })
}

/** Validate one untrusted exact-target override and narrow its public type. */
function assertModelPolicy(
  source: unknown,
  name: string,
): asserts source is ModelCompactPolicyConfig {
  if (!isUnknownRecord(source)) throw new Error(`${name} must be an object`)
  validateKeys(source, MODEL_POLICY_KEYS, name)
  assertNonEmptyString(`${name}.provider`, source.provider)
  assertNonEmptyString(`${name}.model`, source.model)
  validatePolicy(source, name)
}

/** Validate the fields common to defaults and exact-target partial overrides. */
function validatePolicy(
  config: CompactionPolicyConfig | Record<string, unknown>,
  name: string,
): void {
  const thresholdRatio = config.thresholdRatio
  const retainRatio = config.retainRatio
  const retainTokens = config.retainTokens
  const maxTokens = config.maxTokens
  const compactionRetries = config.compactionRetries
  const maxOverflowRetries = config.maxOverflowRetries
  if (thresholdRatio !== undefined) assertRatio(`${name}.thresholdRatio`, thresholdRatio)

View on GitHub (pinned to b150a551b8)

Solutions

  1. Make each list item an object with explicit provider and model fields plus optional policy fields
  2. Remove null or placeholder entries from the list
  3. Check YAML indentation: every item under modelPolicies must start with '- provider:'

Example fix

# before — shorthand string entries
modelPolicies:
  - deepseek/deepseek-chat
  - deepseek/deepseek-reasoner

# after — object entries
modelPolicies:
  - provider: deepseek
    model: deepseek-chat
  - provider: deepseek
    model: deepseek-reasoner
Defensive patterns

Strategy: type-guard

Type guard

function isModelPolicyCandidate(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v)
}
// usage: (config.modelPolicies ?? []).every(isModelPolicyCandidate)

Try / catch

try {
  resolveConfig(config)
} catch (err) {
  const idx = (err as Error).message.match(/modelPolicies\[(\d+)\] must be an object/)?.[1]
  if (idx !== undefined) {
    throw new Error(
      `modelPolicies[${idx}] is not an object: ${JSON.stringify(config.modelPolicies?.[+idx])} — use { provider, model, ...policy }`,
      { cause: err },
    )
  }
  throw err
}

Prevention

When it happens

Trigger: A list item that is a shorthand string (e.g. '- deepseek/deepseek-chat'), a bare scalar ('- 16384'), a null placeholder ('- ~' or an empty '- '), or a nested sequence. Only mapping items ('- provider: x') pass.

Common situations: Trying a compact 'provider/model' string syntax the plugin does not support; YAML indentation that puts a value on its own list item; JSON configs where an entry got replaced by its model name string.

Related errors


AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24). Data as JSON: /api/errors/845d8826a3c1e97a. Report an issue: GitHub.