{"record":{"id":"e2f5b18451820e97","repo":"apache/cassandra","slug":"expected-a-string-representation-of-a-duration-bu","errorCode":null,"errorMessage":"Expected a string representation of a duration, but got a %s: %s","messagePattern":"Expected a string representation of a duration, but got a (.+?): (.+?)","errorType":"validation","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/db/marshal/DurationType.java","lineNumber":72,"sourceCode":"\n        return decompose(Duration.from(source));\n    }\n\n    @Override\n    public boolean isValueCompatibleWithInternal(AbstractType<?> otherType)\n    {\n        return this == otherType;\n    }\n\n    public Term fromJSONObject(Object parsed) throws MarshalException\n    {\n        try\n        {\n            return new Constants.Value(fromString((String) parsed));\n        }\n        catch (ClassCastException exc)\n        {\n            throw new MarshalException(String.format(\"Expected a string representation of a duration, but got a %s: %s\",\n                                                     parsed.getClass().getSimpleName(), parsed));\n        }\n    }\n\n    @Override\n    public TypeSerializer<Duration> getSerializer()\n    {\n        return DurationSerializer.instance;\n    }\n\n    @Override\n    public ArgumentDeserializer getArgumentDeserializer()\n    {\n        return ARGUMENT_DESERIALIZER;\n    }\n\n    @Override\n    public CQL3Type asCQL3Type()","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/db/marshal/DurationType.java#L54-L90","documentation":"DurationType.fromJSONObject() requires the parsed JSON value to be a String, because durations are canonically written as strings like '12h30m' or 'P1Y2M'. If the JSON value is not a String (number, boolean, object, null), ClassCastException is caught and a MarshalException is thrown stating what type was actually received. Durations intentionally do not accept raw numeric JSON values.","triggerScenarios":"INSERT JSON / fromJSONObject with a duration column given as a number (e.g. {\"dur\": 3}) or an object, rather than a duration string like \"3h\".","commonSituations":"Clients encode durations as seconds/milliseconds numbers; ORMs serialize Duration objects to numeric epoch values instead of ISO-8601-like duration strings; schema migration from a bigint column to duration keeps old numeric payloads.","solutions":["Send the duration as a string using Cassandra duration syntax, e.g. \"12h30m\" or \"P1Y2M3DT4H5M6S\"","Convert numeric durations to a duration string (e.g. 3 -> \"3mo\" only if that matches intended unit) before inserting","Fix client serialization so java.time.Duration / custom duration types emit Cassandra-compatible strings"],"exampleFix":"// before\n{\"ttl_duration\": 3600}\n// after\n{\"ttl_duration\": \"1h\"}","handlingStrategy":"type-guard","validationCode":"if (!(value instanceof String)) throw new IllegalArgumentException(\"Duration columns require a string like '12h30m'\");","typeGuard":"boolean isDurationString(Object v) { return v instanceof String && ((String) v).matches(\"(\\\\d+(mo|d|h|m|s|ms|us|ns|y))+\"); }","tryCatchPattern":"try { term = DurationType.instance.fromJSONObject(parsed); } catch (MarshalException e) { log.error(\"Duration must be a string: {}\", parsed); }","preventionTips":["Serialize java.time.Duration to Cassandra duration strings, never epoch numbers","Document duration column format for client teams","Add payload validation before INSERT JSON"],"tags":["cassandra","json","duration"],"backgroundTag":"invalid-argument-format","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"}