{"record":{"id":"0951f28dd7226053","repo":"openzipkin/zipkin","slug":"maximum-skip-depth-exceeded","errorCode":null,"errorMessage":"Maximum skip depth exceeded","messagePattern":"Maximum skip depth exceeded","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/internal/ThriftCodec.java","lineNumber":123,"sourceCode":"    }\n  }\n\n  static IllegalArgumentException exceptionReading(String type, Exception e) {\n    String cause = e.getMessage() == null ? \"Error\" : e.getMessage();\n    if (e instanceof EOFException) cause = \"EOF\";\n    if (e instanceof IllegalStateException || e instanceof BufferUnderflowException) {\n      cause = \"Malformed\";\n    }\n    String message = String.format(\"%s reading %s from TBinary\", cause, type);\n    throw new IllegalArgumentException(message, e);\n  }\n\n  static void skip(ReadBuffer buffer, byte type) {\n    skip(buffer, type, MAX_SKIP_DEPTH);\n  }\n\n  static void skip(ReadBuffer buffer, byte type, int maxDepth) {\n    if (maxDepth <= 0) throw new IllegalStateException(\"Maximum skip depth exceeded\");\n    switch (type) {\n      case TYPE_BOOL:\n      case TYPE_BYTE:\n        buffer.skip(1);\n        break;\n      case TYPE_I16:\n        buffer.skip(2);\n        break;\n      case TYPE_I32:\n        buffer.skip(4);\n        break;\n      case TYPE_DOUBLE:\n      case TYPE_I64:\n        buffer.skip(8);\n        break;\n      case TYPE_STRING:\n        buffer.skip(buffer.readInt());\n        break;","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/internal/ThriftCodec.java#L105-L141","documentation":"When skipping unknown thrift fields, ThriftCodec.skip recurses for nested structs/lists with a depth budget (MAX_SKIP_DEPTH). Each nesting level decrements it; reaching zero throws IllegalStateException('Maximum skip depth exceeded'). This is a bomb-defusal guard against maliciously deep thrift payloads that would cause stack overflow or unbounded CPU.","triggerScenarios":"Decoding a thrift-encoded span containing an unknown list/struct field whose elements are themselves containers, nested deeper than the allowed limit — typical of fuzzed or adversarial input, or corrupt bytes that look like deeply nested containers when mis-skipped.","commonSituations":"A public scribe/collector endpoint receiving crafted spans; corrupt storage blobs where random bytes decode as nested type codes.","solutions":["Treat it as malicious or corrupt input: drop the message, do not retry.","If legitimately deep data is expected (it is not, for zipkin spans), re-encode with the current zipkin thrift schema so no unknown nested fields exist.","Rate-limit and authenticate public ingest endpoints so fuzzed payloads cannot reach the decoder.","Log the payload hash to correlate repeated attack/corruption sources."],"exampleFix":"// before\nSpan span = SpanBytesDecoder.THRIFT.decodeOne(bytes);\n\n// after\nSpan span;\ntry {\n  span = SpanBytesDecoder.THRIFT.decodeOne(bytes);\n} catch (IllegalArgumentException | IllegalStateException e) {\n  LOG.warn(\"rejecting malformed thrift span ({}), dropping\", e.getMessage());\n  span = null;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (IllegalStateException | IllegalArgumentException e) { quarantine(bytes); LOG.warn(\"rejected pathological thrift payload: {}\", e.getMessage()); }","preventionTips":["Authenticate and rate-limit public ingest endpoints.","Treat depth-limit hits as attacks or corruption, never as retryable.","Keep zipkin-server updated — depth guards harden over releases."],"tags":["zipkin","thrift","security","malformed"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}