deepseek-ai/deepseek-harness · error

${name}: retainRatio (${retention.retainRatio}) must be less

Error message

${name}: retainRatio (${retention.retainRatio}) must be less than the resolved thresholdRatio (${thresholdRatio})

What it means

resolveConfig enforces, at plugin load, that the resolved retainRatio stays strictly below the resolved thresholdRatio — for the top level and for every modelPolicies entry (an override's retainRatio is checked against its own or the inherited thresholdRatio). Otherwise the verbatim tail kept after compaction would meet or exceed the pressure that triggers compaction.

Source

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

/** Choose an explicit retention form or inherit the already-resolved fallback. */
function resolveRetention(
  config: CompactionPolicyConfig,
  fallback: ResolvedRetention,
): ResolvedRetention {
  if (config.retainTokens !== undefined) return { retainTokens: config.retainTokens }
  if (config.retainRatio !== undefined) return { retainRatio: config.retainRatio }
  return fallback
}

/** Reject a capacity-independent retention conflict at plugin load. */
function validateRatioRetention(
  thresholdRatio: number,
  retention: ResolvedRetention,
  name: string,
): void {
  if (retention.retainRatio !== undefined && retention.retainRatio >= thresholdRatio) {
    throw new Error(
      `${name}: retainRatio (${retention.retainRatio}) must be less than `
      + `the resolved thresholdRatio (${thresholdRatio})`,
    )
  }
}

/** Validate, detach, and reject duplicate exact-target policies. */
function resolveModelPolicies(configured: unknown): ModelCompactPolicyConfig[] {
  if (configured === undefined) return []
  if (!Array.isArray(configured)) {
    throw new Error('BasicCompactionConfig: modelPolicies must be an array')
  }
  const seen = new Set<string>()
  return configured.map((source: unknown, index) => {
    const name = `BasicCompactionConfig: modelPolicies[${index}]`
    assertModelPolicy(source, name)
    const key = `${source.provider}\u0000${source.model}`
    if (seen.has(key)) {

View on GitHub (pinned to b150a551b8)

Solutions

  1. Keep retainRatio strictly below thresholdRatio (defaults: 0.16 below 0.8).
  2. When lowering thresholdRatio, lower retainRatio in the same change — e.g. thresholdRatio: 0.1 needs retainRatio below 0.1.
  3. If absolute control of the tail is needed, use retainTokens instead — it is mutually exclusive with retainRatio and is validated per target at spec time.

Example fix

# before — default retainRatio 0.16 is not below the lowered threshold
thresholdRatio: 0.1

# after — move both together
thresholdRatio: 0.1
retainRatio: 0.05
Defensive patterns

Strategy: validation

Validate before calling

const thresholdRatio = config.thresholdRatio ?? 0.8
const retainRatio = config.retainRatio ?? 0.16
if (retainRatio >= thresholdRatio) {
  throw new Error('retainRatio must be strictly below thresholdRatio before load')
}

Prevention

When it happens

Trigger: cordis.yml sets retainRatio at or above thresholdRatio directly (thresholdRatio: 0.5, retainRatio: 0.6), or lowers thresholdRatio below the default retainRatio (thresholdRatio: 0.1 with default retainRatio 0.16), or a modelPolicies entry overrides only one side into conflict.

Common situations: Tuning for a larger kept tail and forgetting the ordering constraint; lowering thresholdRatio to compact earlier while keeping default retention; per-model overrides that change one ratio only.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


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