Competitor Ingest Error Taxonomy (CM10)

Status: Draft · Date: 2025-12-04 Scope: Standardize retry/backoff/error taxonomy for the competitor ingest pipeline with deterministic diagnostics.

Objectives

Error Categories

Retryable Errors

CodeCategoryDescriptionMax RetriesBackoff
E1001network_errorNetwork connectivity failure3Exponential
E1002network_timeoutConnection/read timeout3Exponential
E1003rate_limitRate limit exceeded (429)5Linear
E1004service_unavailableService temporarily unavailable (503)3Exponential
E1005transient_ioTemporary I/O error2Fixed
E1006lock_contentionResource lock conflict3Exponential

Non-Retryable Errors

CodeCategoryDescriptionAction
E2001signature_invalidSignature verification failedReject
E2002signature_expiredSignature validity exceededReject
E2003key_unknownSigning key not foundReject
E2004key_expiredSigning key validity exceededReject
E2005key_revokedSigning key revokedReject
E2006alg_unsupportedUnsupported algorithmReject
E2007hash_mismatchContent hash mismatchReject
E2008schema_invalidInput doesn’t match schemaReject
E2009version_unsupportedTool version not supportedReject
E2010no_evidenceNo acceptable evidence foundReject

Warning Conditions

CodeCategoryDescriptionAction
W3001provenance_unknownProvenance not verifiableAccept with warning
W3002degraded_confidenceLow confidence resultAccept with warning
W3003stale_dataData exceeds freshness thresholdAccept with warning
W3004partial_mappingSome fields couldn’t be mappedAccept with warning
W3005deprecated_formatUsing deprecated formatAccept with warning

Error Format

Standard Error Response

{
  "error": {
    "code": "E2001",
    "category": "signature_invalid",
    "message": "DSSE signature verification failed",
    "details": {
      "reason": "Key ID not found in trusted keyring",
      "keyId": "unknown-key-12345",
      "algorithm": "ecdsa-p256"
    },
    "retryable": false,
    "diagnostics": {
      "timestamp": "2025-12-04T12:00:00Z",
      "traceId": "abc123...",
      "inputHash": "b3:...",
      "stage": "signature_verification"
    }
  }
}

Warning Response

{
  "result": {
    "status": "accepted_with_warnings",
    "warnings": [
      {
        "code": "W3001",
        "category": "provenance_unknown",
        "message": "SBOM accepted without verified provenance",
        "details": {
          "reason": "No signature present",
          "fallbackLevel": 2
        }
      }
    ],
    "output": {...}
  }
}

Retry Configuration

Backoff Strategies

{
  "backoff": {
    "exponential": {
      "description": "2^attempt * base, capped at max",
      "config": {
        "base": 1000,
        "factor": 2,
        "max": 60000,
        "jitter": 0.1
      },
      "example": [1000, 2000, 4000, 8000, 16000, 32000, 60000]
    },
    "linear": {
      "description": "initial + (attempt * increment), capped at max",
      "config": {
        "initial": 1000,
        "increment": 1000,
        "max": 30000
      },
      "example": [1000, 2000, 3000, 4000, 5000]
    },
    "fixed": {
      "description": "Constant delay between retries",
      "config": {
        "delay": 5000
      },
      "example": [5000, 5000, 5000]
    }
  }
}

Retry Decision Logic

def should_retry(error: IngestError, attempt: int) -> RetryDecision:
    if error.code.startswith('E2'):
        return RetryDecision(retry=False, reason="Non-retryable error")

    config = RETRY_CONFIG.get(error.category)
    if not config:
        return RetryDecision(retry=False, reason="Unknown error category")

    if attempt >= config.max_retries:
        return RetryDecision(retry=False, reason="Max retries exceeded")

    delay = calculate_backoff(config, attempt)
    return RetryDecision(retry=True, delay=delay)

Diagnostic Output

Deterministic Diagnostics

All error diagnostics must be deterministic and reproducible:

{
  "diagnostics": {
    "timestamp": "2025-12-04T12:00:00Z",
    "traceId": "deterministic-trace-id",
    "inputHash": "b3:...",
    "stage": "normalization",
    "context": {
      "tool": "syft",
      "toolVersion": "1.0.0",
      "adapterVersion": "1.0.0",
      "inputSize": 12345,
      "componentCount": 42
    },
    "errorChain": [
      {
        "stage": "schema_validation",
        "error": "Missing required field: artifacts[0].purl",
        "path": "/artifacts/0"
      }
    ]
  }
}

Offline Diagnostics

For offline mode, diagnostics include:

{
  "diagnostics": {
    "mode": "offline",
    "kitVersion": "1.0.0",
    "traceId": "b3:input-hash-derived-trace-id",
    "kitHash": "b3:...",
    "trustRootHash": "b3:..."
  }
}

Error Handling Workflow

┌─────────────┐
│   Ingest    │
│   Request   │
└─────────────┘
       │
       ▼
┌─────────────┐
│  Validate   │──Error──► E2008 schema_invalid
└─────────────┘
       │
       ▼
┌─────────────┐
│   Verify    │──Error──► E2001-E2007 signature errors
│  Signature  │
└─────────────┘
       │
       ▼
┌─────────────┐
│  Normalize  │──Error──► E2008-E2009 format errors
└─────────────┘
       │
       ▼
┌─────────────┐
│   Store     │──Error──► E1001-E1006 retryable errors
└─────────────┘
       │
       ▼
┌─────────────┐
│   Success   │
│  or Warn    │
└─────────────┘

API Error Responses

HTTP Status Mapping

Error CategoryHTTP StatusResponse Body
Retryable (E1xxx)503Error with Retry-After header
Rate limit (E1003)429Error with Retry-After header
Signature (E2001-E2007)400Error with details
Schema (E2008)400Error with validation details
Version (E2009)400Error with supported versions
No evidence (E2010)400Error with fallback options

Example Error Response

HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Stellaops-Error-Code: E2001
X-StellaOps-TraceId: abc123...

{
  "error": {
    "code": "E2001",
    "category": "signature_invalid",
    "message": "DSSE signature verification failed",
    "details": {...},
    "retryable": false,
    "diagnostics": {...}
  }
}

Retry Response

HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Retry-After: 5
X-Stellaops-Error-Code: E1004
X-Stellaops-Retry-Attempt: 1

{
  "error": {
    "code": "E1004",
    "category": "service_unavailable",
    "message": "Upstream service temporarily unavailable",
    "retryable": true,
    "retryAfter": 5000,
    "attempt": 1,
    "maxAttempts": 3
  }
}

Logging

Error Log Format

{
  "level": "error",
  "timestamp": "2025-12-04T12:00:00Z",
  "logger": "stellaops.scanner.ingest",
  "message": "Ingest failed: signature_invalid",
  "error": {
    "code": "E2001",
    "category": "signature_invalid"
  },
  "context": {
    "traceId": "abc123...",
    "tool": "syft",
    "inputHash": "b3:..."
  }
}