Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/observability-nested-null-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cloudflare/workers-utils": patch
---

Report a null `observability.logs`/`observability.traces` as a config error instead of crashing

`{ "observability": { "enabled": true, "logs": null } }` crashed with `TypeError: Cannot read properties of null (reading 'enabled')` instead of producing a validation error. Because `typeof null === "object"`, null passed the type check for these nested objects and then the per-property checks dereferenced it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Release note explains internal code details instead of user impact

The release note text includes an internal implementation explanation (Because "typeof null === \"object\""... at .changeset/observability-nested-null-validation.md:7) instead of only describing what changes for people using the tool, so the published changelog reads like maintainer notes.
Impact: Users reading the changelog see internal reasoning rather than a clear statement of what was fixed.

Repository changeset rule being violated

REVIEW.md states: "Changesets should target users of the tools (e.g. Wrangler users) rather than maintainers. Avoid including implementation details ... Instead, focus on user-facing impact and benefits." The middle paragraph of the changeset explains the JavaScript typeof null quirk and internal per-property dereferencing, which is maintainer-facing detail.

Suggested change
`{ "observability": { "enabled": true, "logs": null } }` crashed with `TypeError: Cannot read properties of null (reading 'enabled')` instead of producing a validation error. Because `typeof null === "object"`, null passed the type check for these nested objects and then the per-property checks dereferenced it.
Setting `observability.logs` or `observability.traces` to `null` in your Wrangler configuration previously crashed with an unhandled error instead of reporting a configuration problem.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


Null is now rejected with a normal config error, matching how a null top-level `observability` is already handled.
18 changes: 16 additions & 2 deletions packages/workers-utils/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6416,7 +6416,14 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
/**
* Validate the optional nested logs configuration
*/
if (typeof val.logs === "object") {
if (val.logs === null) {
// `typeof null === "object"`, so the check above accepts null; reject it
// here rather than crashing on the nested property checks below.
diagnostics.errors.push(
`Expected "${field}.logs" to be of type object but got null.`
);
isValid = false;
} else if (typeof val.logs === "object") {
isValid =
validateOptionalProperty(
diagnostics,
Expand Down Expand Up @@ -6474,7 +6481,14 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
/**
* Validate the optional nested traces configuration
*/
if (typeof val.traces === "object") {
if (val.traces === null) {
// `typeof null === "object"`, so the check above accepts null; reject it
// here rather than crashing on the nested property checks below.
diagnostics.errors.push(
`Expected "${field}.traces" to be of type object but got null.`
);
isValid = false;
} else if (typeof val.traces === "object") {
isValid =
validateOptionalProperty(
diagnostics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10226,6 +10226,42 @@ describe("normalizeAndValidateConfig()", () => {
`);
});

it("should error if observability.logs is null", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
observability: { enabled: true, logs: null },
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- Expected "observability.logs" to be of type object but got null."
`);
});

it("should error if observability.traces is null", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
observability: { enabled: true, traces: null },
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- Expected "observability.traces" to be of type object but got null."
`);
});

it("should not warn on full observability config", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
Expand Down
Loading