deepseek-ai/deepseek-harness · error

${name} must be a non-empty string

Error message

${name} must be a non-empty string

What it means

Inside each modelPolicies entry, provider and model form the exact route key and must be non-empty strings (assertNonEmptyString). The check fires for '' and for non-strings (numbers, booleans, null) alike, because an empty route can never match a real provider/model. The name in the message is 'BasicCompactionConfig: modelPolicies[<i>].provider' or '.model'.

Source

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

      + 'as an empty or non-empty pair',
    )
  }
}

/** Reject stale or misspelled keys before defaults can hide them. */
function validateKeys(config: object, keys: ReadonlySet<string>, name: string): void {
  for (const key of Object.keys(config)) {
    if (!keys.has(key)) throw new Error(`${name}: unknown key "${key}"`)
  }
}

function isUnknownRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function assertNonEmptyString(name: string, value: unknown): asserts value is string {
  if (typeof value !== 'string' || value.length === 0) {
    throw new Error(`${name} must be a non-empty string`)
  }
}

function assertPositiveInteger(name: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
    throw new Error(`${name} (${String(value)}) must be a positive integer`)
  }
}

function assertNonNegativeInteger(name: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
    throw new Error(`${name} (${String(value)}) must be a non-negative integer`)
  }
}

function assertRatio(name: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value > 1) {
    throw new Error(`${name} (${String(value)}) must be a number in (0, 1]`)

View on GitHub (pinned to b150a551b8)

Solutions

  1. Fill in real non-empty provider and model strings
  2. Remove placeholder entries until the route is known
  3. Guard env/template-driven config so an empty substitution drops the whole entry instead of shipping it

Example fix

# before — empty route key
modelPolicies:
  - provider: deepseek
    model: ''
    maxTokens: 16384

# after
modelPolicies:
  - provider: deepseek
    model: deepseek-chat
    maxTokens: 16384
Defensive patterns

Strategy: type-guard

Type guard

function isNonEmptyString(v: unknown): v is string {
  return typeof v === 'string' && v.length > 0
}
// usage: modelPolicies.every(p => isNonEmptyString(p.provider) && isNonEmptyString(p.model))

Try / catch

try {
  resolveConfig(config)
} catch (err) {
  const m = (err as Error).message
  const hit = m.match(/modelPolicies\[(\d+)\]\.(provider|model) must be a non-empty string/)
  if (hit !== undefined) {
    throw new Error(
      `modelPolicies[${hit[1]}].${hit[2]} is empty or not a string — fill in the real route value`,
      { cause: err },
    )
  }
  throw err
}

Prevention

When it happens

Trigger: A modelPolicies entry with 'provider:' or 'model:' left empty in YAML (null), set to '', or set to a non-string value; template or env substitution producing an empty identifier.

Common situations: Placeholder entries awaiting real model names; env-driven config where the variable is unset and interpolates to ''; refactoring that moves provider into a variable that is sometimes empty.

Related errors


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