{"record":{"id":"52610b904a564a74","repo":"goharbor/harbor","slug":"purge-upload-age-should-set-with-with-nh-n-is-the-52610b","errorCode":null,"errorMessage":"purge upload age should set with with nh, n is the number of hour and n should not be less than 2","messagePattern":"purge upload age should set with with nh, n is the number of hour and n should not be less than 2","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"make/photon/prepare/models.py","lineNumber":218,"sourceCode":"    def __init__(self, config: dict):\n        if not config:\n            self.enabled = False\n        self.enabled = config.get('enabled')\n        self.age = config.get('age') or '168h'\n        self.interval = config.get('interval') or '24h'\n        self.dryrun = config.get('dryrun') or False\n        return\n\n    def validate(self):\n        if not self.enabled:\n            return\n        # age should end with h\n        if not isinstance(self.age, str) or not self.age.endswith('h'):\n            raise Exception('purge upload age should set with with nh, n is the number of hour')\n        # interval should larger than 2h\n        age = self.age[:-1]\n        if not age.isnumeric() or int(age) < 2:\n            raise Exception('purge upload age should set with with nh, n is the number of hour and n should not be less than 2')\n\n        # interval should end with h\n        if not isinstance(self.interval, str) or not self.interval.endswith('h'):\n            raise Exception('purge upload interval should set with with nh, n is the number of hour')\n        # interval should larger than 2h\n        interval = self.interval[:-1]\n        if not interval.isnumeric() or int(interval) < 2:\n            raise Exception('purge upload interval should set with with nh, n is the number of hour and n should not beless than 2')\n        return\n\n\nclass Cache:\n    def __init__(self, config: dict):\n        if not config:\n            self.enabled = False\n        self.enabled = config.get('enabled')\n        self.expire_hours = config.get('expire_hours')\n","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/make/photon/prepare/models.py#L200-L236","documentation":"The SBOM report DAO's DeleteMany refuses to run when the query carries no keywords. With an empty query, orm.QuerySetter would match every row of the sbom report table, so this error is an explicit guard against an accidental full-table wipe. It is intentional data protection, not a bug.","triggerScenarios":"dao.DeleteMany(ctx, q.Query{}) or a q.Query whose Keywords map is nil/empty — e.g. a cleanup job built its digest filter conditionally and the condition never fired.","commonSituations":"GC or retention jobs constructing keyword filters from possibly-empty inputs; nil-vs-empty-map bugs after refactors; code ported from the vulnerability report DAO assuming different query semantics.","solutions":["Always set at least one keyword (digest, registration_uuid, ...) before calling DeleteMany","Assert the filter is non-empty in the layer above the DAO and fail with context","Keep the guard as is — fix the empty input, never bypass the check"],"exampleFix":"// before\n_, err := sbomDAO.DeleteMany(ctx, query) // query.Keywords may be empty\n\n// after\nif len(query.Keywords) == 0 {\n    return errors.New(\"refusing to delete sbom reports: no filter set\")\n}\n_, err := sbomDAO.DeleteMany(ctx, query)","handlingStrategy":"validation","validationCode":"if len(query.Keywords) == 0 {\n    return fmt.Errorf(\"refusing bulk delete: no keywords in query\")\n}\nreturn d.DeleteMany(ctx, query)","typeGuard":null,"tryCatchPattern":"if _, err := d.DeleteMany(ctx, query); err != nil {\n    if strings.Contains(err.Error(), \"delete all sbom reports at once is not allowed\") {\n        // guard fired: your filter construction is broken — never bypass it\n        return fmt.Errorf(\"cleanup filter empty; aborting delete\")\n    }\n    return err\n}","preventionTips":["Build delete filters from values you have already asserted non-empty","Treat this error as a filter-construction bug, not as a DAO limitation","Never attempt a full-table delete through this DAO"],"tags":["go","harbor","sbom","dao","data-safety","validation"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}