deepseek-ai/deepseek-harness · error

${name}: unknown key "${key}"

Error message

${name}: unknown key "${key}"

What it means

Configuration uses strict unknown-key rejection: validateKeys() throws on the first key outside the allowed set, before defaults can hide the mistake. Top level accepts thresholdRatio, retainRatio, retainTokens, summarizationProvider, summarizationModel, maxTokens, compactionRetries, maxOverflowRetries, modelPolicies, and auto; a modelPolicies entry accepts provider and model plus the eight policy fields (not auto).

Source

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

    throw new Error(`${name}.summarizationProvider must be a string`)
  }
  if (model !== undefined && typeof model !== 'string') {
    throw new Error(`${name}.summarizationModel must be a string`)
  }
  if (provider === undefined && model === undefined) return
  if (provider === undefined || model === undefined
    || (provider.length === 0) !== (model.length === 0)) {
    throw new Error(
      `${name}: summarizationProvider and summarizationModel must be set together `
      + '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`)
  }
}

View on GitHub (pinned to b150a551b8)

Solutions

  1. Fix the rejected key named in the message against the allowed set for that scope (e.g. maxToken to maxTokens)
  2. Move scope-bound keys to the correct block: auto and modelPolicies are top-level; provider and model belong to entries
  3. Delete keys the current version does not support instead of leaving them in place

Example fix

# before
modelPolicies:
  - provider: deepseek
    model: deepseek-chat
    maxToken: 16384   # typo: unknown key
    auto: false       # wrong scope: top-level only

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

Strategy: validation

Validate before calling

const POLICY_KEYS = new Set([
  'thresholdRatio', 'retainRatio', 'retainTokens',
  'summarizationProvider', 'summarizationModel',
  'maxTokens', 'compactionRetries', 'maxOverflowRetries',
])
const TOP_KEYS = new Set([...POLICY_KEYS, 'modelPolicies', 'auto'])
const ENTRY_KEYS = new Set([...POLICY_KEYS, 'provider', 'model'])

function checkKeys(obj: object, allowed: Set<string>, where: string) {
  for (const k of Object.keys(obj)) {
    if (!allowed.has(k)) throw new Error(`${where}: unknown key ${k}`)
  }
}

checkKeys(config, TOP_KEYS, 'config')
for (const [i, e] of (config.modelPolicies ?? []).entries()) {
  checkKeys(e, ENTRY_KEYS, `modelPolicies[${i}]`)
}

Try / catch

try {
  resolveConfig(config)
} catch (err) {
  const key = (err as Error).message.match(/unknown key "(.+?)"/)?.[1]
  if (key !== undefined) {
    throw new Error(
      `config key ${key} is misspelled, stale, or in the wrong scope — compare with the allowed key set`,
      { cause: err },
    )
  }
  throw err
}

Prevention

When it happens

Trigger: A misspelled key (retainToken, thresoldRatio, summarisationModel), a stale key from another version, or a key nested in the wrong scope: 'auto' inside a modelPolicies entry, or 'modelPolicies' nested inside an entry.

Common situations: Version upgrades that renamed keys; copy-paste between different plugins' config blocks; editor autocompletion inserting a plausible-but-wrong field name.

Related errors


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