{"record":{"id":"ac2ccf3ca9f2887f","repo":"apify/crawlee","slug":"the-custom-statistics-field-string-key-colli","errorCode":null,"errorMessage":"The custom statistics field `${String(key)}` collides with a built-in one - it would shadow the value the crawler tracks. Rename it in `stateExtension`.","messagePattern":"The custom statistics field `(.+?)` collides with a built-in one - it would shadow the value the crawler tracks\\. Rename it in `stateExtension`\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/crawlers/statistics.ts","lineNumber":361,"sourceCode":"\n        this.id = id ?? String(Statistics.#id++);\n        this.#persistStateKey = `CRAWLEE_CRAWLER_STATISTICS_${this.id}`;\n\n        this.log = (log ?? serviceLocator.getLogger()).child({ prefix: 'Statistics' });\n        this.errorTracker = new ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });\n        this.errorTrackerRetry = new ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });\n        this.#logIntervalMillis = logIntervalSecs * 1000;\n        this.#logMessage = logMessage;\n        this.#stateExtension = stateExtension as StatisticStateExtensionOptions<\n            StateExtension,\n            PersistedStateExtension\n        >;\n        this.#defaultStateExtension = this.#resolveDefaultStateExtension(this.#stateExtension);\n        this.#stateExtensionKeys = Object.keys(this.#defaultStateExtension()) as (keyof StateExtension)[];\n\n        for (const key of this.#stateExtensionKeys) {\n            if ((key as string) in this.#builtInDefaultState()) {\n                throw new Error(\n                    `The custom statistics field \\`${String(key)}\\` collides with a built-in one - it would shadow ` +\n                        'the value the crawler tracks. Rename it in `stateExtension`.',\n                );\n            }\n        }\n\n        // `calculate()` is late-bound on purpose - it is an override point, and a subclass's must be the one that runs.\n        this.#stateCodec = buildStatisticStateCodec({\n            statsId: this.id,\n            defaultState: () => this.#defaultState(),\n            calculate: () => this.calculate(),\n        });\n\n        this.#recoverableState = new RecoverableState({\n            persistStateKey: this.#persistStateKey,\n            persistenceEnabled: persistenceOptions.enable,\n            keyValueStore,\n            logger: this.log,","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/core/src/crawlers/statistics.ts#L343-L379","documentation":"Statistics supports custom tracked fields via a stateExtension. This error is thrown in the Statistics constructor when a custom field name matches one of the built-in statistics fields, because it would shadow values the crawler tracks internally. Rename the custom field to avoid the collision.","triggerScenarios":"Passing a stateExtension whose deserialized/default state contains a key present in the built-in default state (e.g. `errors`, `requestsFinished`, `crawlerStartedAt`).","commonSituations":"Extending crawler stats with common field names after migrating code; copying built-in field names into custom extensions; merging two configs where a custom field collides with newer built-ins after a library upgrade.","solutions":["Rename the colliding key in stateExtension (deserialize/defaultState)","Log Object.keys(built-in default state) to compare against your custom keys","Pick a prefixed naming convention for custom fields (e.g. `custom_` prefix)","After library upgrades, re-check for collisions with newly added built-in fields"],"exampleFix":"// before\nstateExtension: { deserialize: (s) => ({ errors: s.e }) }\n// after\nstateExtension: { deserialize: (s) => ({ customErrors: s.e }) }","handlingStrategy":"validation","validationCode":"const builtIn = Object.keys(crawler.stats.state); // inspect before naming custom fields\nconst clash = Object.keys(myDefaults).filter((k) => builtIn.includes(k));\nif (clash.length) throw new Error(`rename custom fields: ${clash}`);","typeGuard":null,"tryCatchPattern":"try { new Statistics({ stateExtension }); } catch (e) { if (String(e).includes('collides with a built-in')) renameFields(); else throw e; }","preventionTips":["Prefix custom stat fields (e.g. custom_)","Re-check names after upgrading the library","Diff custom keys against built-in default state in tests"],"tags":["statistics","configuration","naming-collision"],"backgroundTag":"state-key-collision","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}