{"record":{"id":"15cb5c79f32c0a64","repo":"apache/cassandra","slug":"invalid-remaining-data-after-end-of-s-value","errorCode":null,"errorMessage":"Invalid remaining data after end of %s value","messagePattern":"Invalid remaining data after end of (.+?) value","errorType":"validation","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/marshal/TupleType.java","lineNumber":337,"sourceCode":"            position += 4;\n\n            // size < 0 means null value\n            if (size >= 0)\n            {\n                if (length - position < size)\n                    throw new MarshalException(String.format(\"Not enough bytes to read %dth %s\", i, componentOrFieldName(i)));\n\n                components.add(accessor.slice(value, position, size));\n                position += size;\n            }\n            else\n                components.add(null);\n        }\n\n        // error out if we got more values in the tuple/UDT than we expected\n        if (position < length)\n        {\n            throw new MarshalException(String.format(\"Invalid remaining data after end of %s value\", isTuple() ? \"tuple\" : \"UDT\"));\n        }\n\n        return components;\n    }\n\n    /**\n     * Returns the name used for the specified component or field if the type is a Tuple.\n     * @param i the component/field index\n     * @return the name used for the specified component or field if the type is a Tuple.\n     */\n    protected String componentOrFieldName(int i)\n    {\n        return \"component\";\n    }\n\n    public static <V> V pack(ValueAccessor<V> accessor, V... components)\n    {\n        return pack(accessor, Arrays.asList(components));","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/marshal/TupleType.java#L319-L355","documentation":"After unpacking all declared components of a tuple/UDT, TupleType checks that no leftover bytes remain in the buffer. If position < length, the value has more data than the type declares components, so unpack throws this MarshalException. It guards against reading a value serialized for a type with more fields than the current definition.","triggerScenarios":"Calling unpack/elements/components on a serialized value that contains extra trailing bytes beyond the last declared component — e.g. a tuple written with N+1 fields then read as an N-field tuple after a schema downgrade, or concatenating wrong buffers.","commonSituations":"UDT/tuple schema evolved to fewer fields while old rows still hold the wider serialization; manual blob assembly mistakes; reading data from another cluster with a different UDT definition.","solutions":["Confirm the reading type matches the schema that serialized the value (same number of fields)","Migrate/rewrite rows written under the old wider UDT definition","Re-serialize the value from its parsed fields instead of reusing the raw buffer","If intentional cross-version data, handle the old format explicitly before calling unpack"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (buf.remaining() > tupleType.serializedSize(...))\n    throw new IllegalArgumentException(\"value longer than declared tuple serialization\");","typeGuard":null,"tryCatchPattern":"try {\n    tupleType.unpack(buf);\n} catch (MarshalException e) {\n    // leftover bytes: schema mismatch between reader and writer\n    log.error(\"Tuple type mismatch: {}\", e.getMessage());\n}","preventionTips":["Keep UDT/tuple definitions in sync across clusters before migrating data","Rewrite rows after shrinking a tuple/UDT field count","Do not concatenate buffers from different tuple values"],"tags":["deserialization","tuple","schema-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}