{"record":{"id":"a4fd1436813e7856","repo":"Hmbown/CodeWhale","slug":"post","errorCode":null,"errorMessage":"POST","messagePattern":"POST","errorType":"http","errorClass":null,"httpStatus":405,"severity":"info","filePath":"telemetry-ingest/src/index.ts","lineNumber":128,"sourceCode":"      chunks.push(value);\n    }\n  } finally {\n    reader.releaseLock();\n  }\n  const joined = new Uint8Array(total);\n  let offset = 0;\n  for (const chunk of chunks) {\n    joined.set(chunk, offset);\n    offset += chunk.byteLength;\n  }\n  return joined;\n}\n\nasync function ingest(request: Request, env: Env): Promise<Response> {\n  // Method before path, so a probe of any path with any verb other than POST\n  // gets the same answer and learns nothing about what exists here.\n  if (request.method !== \"POST\") {\n    return status(405, { allow: \"POST\" });\n  }\n  if (new URL(request.url).pathname !== INGEST_PATH) {\n    return status(404);\n  }\n\n  // Header read #1 of 2. `client.rs` sends exactly `application/json`.\n  const contentType = request.headers.get(\"content-type\") ?? \"\";\n  if (!contentType.toLowerCase().startsWith(\"application/json\")) {\n    return status(415);\n  }\n\n  // Header read #2 of 2, and the last. See the red line above.\n  const declared = request.headers.get(\"content-length\");\n  if (declared !== null) {\n    const length = Number(declared);\n    if (!Number.isFinite(length) || length > MAX_BODY_BYTES) {\n      return status(413);\n    }","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/telemetry-ingest/src/index.ts#L110-L146","documentation":"The telemetry-ingest worker's ingest() deliberately returns 405 with an `allow: POST` header for any non-POST request, before checking the path. This is intentional: probing any path with any verb other than POST yields the same answer so scanners learn nothing about existing endpoints. It surfaces as the HTTP error \"POST\" reported at the ingest boundary.","triggerScenarios":"Sending GET/PUT/DELETE/HEAD (or any method other than POST) to the ingest worker, including health checks or browser navigation to the ingest URL.","commonSituations":"Configuring a monitor or uptime checker that pings the endpoint with GET, a client using the wrong HTTP verb, or a user opening the ingest URL in a browser.","solutions":["Send requests as POST to the ingest path","Point health checks at a dedicated health endpoint instead of the ingest path","Update the client so it uses method: \"POST\" with a JSON body","Expect a 405 with allow: POST header when probing with other verbs — this is by design"],"exampleFix":"// before\nawait fetch(url); // GET\n// after\nawait fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify(payload) });","handlingStrategy":"try-catch","validationCode":"if (method.toUpperCase() !== \"POST\") {\n  throw new Error(\"telemetry ingest requires POST\");\n}","typeGuard":null,"tryCatchPattern":"const res = await fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body });\nif (res.status === 405) {\n  // wrong verb; switch to POST\n}\nif (!res.ok) throw new Error(`ingest failed: ${res.status}`);","preventionTips":["Always use POST for telemetry submission","Point health/uptime monitors at a health endpoint, not ingest","Expect 405 + allow: POST when probing with other verbs — do not treat as outage","Send content-type: application/json exactly"],"tags":["http","method-not-allowed","worker"],"backgroundTag":"http-error-response","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}