{"record":{"id":"6c50fccae837501e","repo":"usememos/memos","slug":"modulo-by-zero","errorCode":null,"errorMessage":"modulo by zero","messagePattern":"modulo by zero","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/filter/parser.go","lineNumber":578,"sourceCode":"\t\t}\n\t\tif !ok {\n\t\t\treturn 0, false, nil\n\t\t}\n\t\tswitch call.Function {\n\t\tcase \"_+_\":\n\t\t\treturn left + right, true, nil\n\t\tcase \"_-_\":\n\t\t\treturn left - right, true, nil\n\t\tcase \"_*_\":\n\t\t\treturn left * right, true, nil\n\t\tcase \"_/_\":\n\t\t\tif right == 0 {\n\t\t\t\treturn 0, false, errors.New(\"division by zero\")\n\t\t\t}\n\t\t\treturn left / right, true, nil\n\t\tcase \"_%_\":\n\t\t\tif right == 0 {\n\t\t\t\treturn 0, false, errors.New(\"modulo by zero\")\n\t\t\t}\n\t\t\treturn left % right, true, nil\n\t\tdefault:\n\t\t\treturn 0, false, errors.Errorf(\"unsupported arithmetic operator %q\", call.Function)\n\t\t}\n\tdefault:\n\t\treturn 0, false, nil\n\t}\n}\n\n// evaluateTimestamp folds timestamp(\"RFC3339\") and timestamp(<epoch int>) into\n// Unix epoch seconds.\nfunc evaluateTimestamp(call *exprv1.Expr_Call) (int64, bool, error) {\n\tif len(call.Args) != 1 {\n\t\treturn 0, false, errors.New(\"timestamp() expects one argument\")\n\t}\n\tvalue, err := getConstValue(call.Args[0])\n\tif err != nil {","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/filter/parser.go#L560-L596","documentation":"Same compile-time constant folding path as division, but for the modulo operator '_%_': the right operand folded to zero and integer modulo by zero has no value, so the filter fails to compile.","triggerScenarios":"Filters such as created_ts % 0 == 0 or (now - duration(\"1h\")) % 0 > 0 where the modulus folds to the constant 0.","commonSituations":"Template-generated modulo expressions with a configurable modulus that defaults to 0; leftovers from bucketing logic (bucket by N seconds) where N was never filled in.","solutions":["Set a non-zero modulus literal in the filter","Default the modulus config value to something positive (e.g. 60) in the generator","Replace modulo bucketing with range comparisons if the modulus is dynamic"],"exampleFix":"// before\ncreated_ts % 0 == 0\n\n// after\ncreated_ts % 60 == 0","handlingStrategy":"validation","validationCode":"// TS: reject zero moduli in generated filters\nconst modulus = Number(cfg.modulus);\nif (!(Number.isInteger(modulus) && modulus > 0)) {\n  throw new Error('modulus must be a positive integer');\n}","typeGuard":"const isPositiveInt = (v: unknown): v is number => Number.isInteger(v) && (v as number) > 0;","tryCatchPattern":null,"preventionTips":["Default bucketing moduli to positive constants (e.g. 60)","Validate modulus parameters at the config layer","Prefer range comparisons over modulo for time bucketing when possible"],"tags":["cel","arithmetic","modulo","filter"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}