stablyai/orca · error

Targeted update changed the non-targeted control topology

Error message

Targeted update changed the non-targeted control topology

What it means

Third isolation rail for the control skill in symlink shape: after a targeted update, lstat(controlProvider) must still report a symbolic link. If the targeted update replaced the control provider symlink with a copy (or vice versa), topology changed and the throw fires.

Source

Thrown at config/scripts/verify-skill-update-roundtrip.mjs:245

  if (shape === 'copy') {
    // Why: hosted 1.5.17 replaces copies with aliases while equivalent local runs
    // retain the copy. Both prove this input topology must remain ineligible.
    const outcome = targetProviderStat.isSymbolicLink()
      ? 'converged to an alias'
      : targetProviderAfter === targetProviderBefore
        ? 'remained a historical copy'
        : 'converged as a copy'
    console.log(`[skill-update-roundtrip] independent copy ${outcome}`)
  }
  if ((await packageDigestAt(controlCanonical)) !== controlBefore) {
    throw new Error('Targeted update changed the non-targeted control skill')
  }
  if ((await packageDigestAt(await realpath(controlProvider))) !== controlProviderBefore) {
    throw new Error('Targeted update changed the non-targeted control provider placement')
  }
  const controlProviderStat = await lstat(controlProvider)
  if (shape === 'symlink' && !controlProviderStat.isSymbolicLink()) {
    throw new Error('Targeted update changed the non-targeted control topology')
  }
} finally {
  await rm(sandbox, { recursive: true, force: true })
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. On Windows, ensure the runner has symlink privilege (Developer Mode or admin) so the control symlink is created and preserved.
  2. Diff lstat of controlProvider before/after to see if topology flipped.
  3. File a CLI bug: a targeted update must not alter non-target topology.
  4. Pin a known-good CLI version.
Defensive patterns

Strategy: validation

Validate before calling

// On Windows, refuse to run shape=symlink unless symlink privilege is available:
if (process.platform === 'win32' && shape === 'symlink') {
  try {
    const probe = path.join(sandbox, 'probe')
    await symlink(targetName, probe); await rm(probe)
  } catch {
    throw new Error('Windows runner lacks symlink privilege; cannot exercise shape=symlink')
  }
}

Prevention

When it happens

Trigger: A targeted update that normalizes topology across all placements (e.g. converts all symlinks to copies) instead of only the target; symlink shape running on a platform/filesystem that silently resolves symlinks on write.

Common situations: CLI regression that re-homogenizes topology on update; Windows without symlink privilege causing the placement to fall back to copy; filesystem that doesn't preserve symlinks.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/ce0f47ffefd58c13. Report an issue: GitHub.