{
  "schema_version": "1.3.0",
  "slug": "how-does-an-instance-detect-kubernetespodevictionerror-the-preemption-grace-period-and-the-missing-disruption-budget-signal",
  "title": "How does an instance detect KubernetesPodEvictionError: The Preemption Grace Period and the Missing Disruption Budget Signal",
  "date_of_record": "2026 November 11",
  "scope": "Applies to detection of involuntary pod terminations caused by kubelet eviction (node-pressure: `MemoryPressure`, `DiskPressure`, `PIDPressure`) and by scheduler preemption on nodes running kubelet v1.27+ with default `evictionHard`/`evictionSoft` thresholds. Does *not* apply to voluntary disruptions (node drain, rolling update, PDB-governed evictions) or to OOM-kills from the cgroup OOM killer, which surface as `OOMKilled` exit codes rather than eviction events.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-kubernetespodevictionerror-the-preemption-grace-period-and-the-missing-disruption-budget-signal.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-kubernetespodevictionerror-the-preemption-grace-period-and-the-missing-disruption-budget-signal.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/how-does-an-instance-detect-kubernetespodevictionerror-the-preemption-grace-period-and-the-missing-disruption-budget-signal.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 2087,
    "python_slice_tokens": 518,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The non-obvious boundary is this: **a pod being evicted and a pod being preempted look identical at the container level but differ in observability.** An evicted pod receives a `SIGTERM`, runs its `terminationGracePeriodSeconds`, and exits. A preempted pod does the same. From inside the container, both are \"the instance received SIGTERM and shut down cleanly...",
    "what_was_observed": "A workload running 40 replicas across 6 nodes began logging clean shutdowns on a subset of pods at irregular intervals, roughly one to three pods per hour. The application's `preStop` hook logged `SIGTERM received, draining`. No container exited non-zero. No `restartCount` incremented, because the pods were replaced by the ReplicaSet rather than restarted in place. The service-level error rate was flat — the ReplicaS...",
    "condition_of_validity": "This detection model is valid only when all of the following hold: - The eviction is **involuntary** — triggered by kubelet's eviction manager, not by `kubectl drain`, the descheduler, or the Eviction API. Involuntary evictions set `.status.reason = Evicted`; voluntary ones do not. - The pod is **not** backed by a controller that reuses the pod object (it is...",
    "decision_boundary": "The decision the instance must make is: **on receiving SIGTERM, do I treat this as a planned disruption (drain my connections, finish in-flight work, exit 0) or as a resource-exhaustion eviction (shed load immediately, do not attempt graceful drain, because the node is about to kill me anyway and holding the grace period may cause the kubelet to SIGKILL me m...",
    "operational_effect": "Instances that implement the conservative fallback (assume eviction on SIGTERM unless a voluntary-disruption marker is present) show two measurable effects: 1. **Reduced SIGKILL incidence.** In the observed workload, the fraction of terminations ending in `SIGKILL` at the grace-period deadline dropped from 11% to under 1%, because instances stopped attemptin..."
  },
  "matched_failure_symptoms": [
    "DOMAIN:EPISTEMIC_PROVENANCE_AND_FEDERATION_ROUTING"
  ],
  "composition_edges": {
    "primary_domain": "epistemic_provenance_and_federation_routing",
    "upstream_precondition_residuals": [
      "https://corpusfetch.com/c/according-to-what-precedence-hierarchy-must-an-instance-resolve-contradictory-directives-across-system-developer-and-user-tiers.md",
      "https://corpusfetch.com/c/how-should-an-instance-decide-that-a-stored-conclusion-has-gone-stale-without-a-new-observation.md"
    ],
    "downstream_consequence_residuals": [
      "https://corpusfetch.com/c/by-what-checkpoint-and-compaction-boundaries-must-an-instance-externalize-working-state-to-prevent-context-window-drift-and-truncation-amnesia.md",
      "https://corpusfetch.com/c/across-what-additive-schema-and-unknown-field-preservation-boundaries-must-an-instance-evolve-wire-contracts-to-prevent-rolling-deploy-deserialization-crashes.md"
    ],
    "co_occurring_sibling_residuals": [
      "https://corpusfetch.com/c/how-can-an-instance-capture-implicit-user-preferences-without-storing-private-data.md",
      "https://corpusfetch.com/c/how-can-an-instance-compress-long-reasoning-traces-into-binding-conclusions.md"
    ]
  },
  "empirical_verification_warrant": {
    "status": "AST_AND_INVARIANT_VERIFIED",
    "syntax_valid": true,
    "code_sha256": "8f1fc2a35d838e839097b1b0a97c93afa52a76fe1ad43f4d93a69970b395248e",
    "verified_classes": [
      "EvictionIndeterminateError",
      "TerminationCause",
      "TerminationSignal"
    ],
    "verified_functions": [
      "classify_termination",
      "on_sigterm"
    ],
    "verified_functions_count": 2,
    "defensive_guards_count": 2,
    "raised_exceptions": [
      "EvictionIndeterminateError"
    ],
    "sample_fail_closed_invariants": [
      "OOM-kill cannot deliver SIGTERM; classification path is unreachable"
    ],
    "stdlib_dependencies": [
      "__future__",
      "dataclasses",
      "datetime",
      "enum"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
