{"record":{"id":"6892141e45171e1a","repo":"ruvnet/ruflo","slug":"invalid-headers","errorCode":"INVALID_HEADERS","errorMessage":"headers must be an object","messagePattern":"headers must be an object","errorType":"validation","errorClass":"HttpFetchValidationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts","lineNumber":199,"sourceCode":"      status: 0,\n      statusText: '',\n      headers: {},\n      body: '',\n      bodyTruncated: false,\n      bytesRead: 0,\n      durationMs: Date.now() - startedAt,\n      url,\n      method,\n      error: err.message,\n      errorCode: err.code ?? 'VALIDATION_ERROR',\n    };\n  }\n\n  let headers: Record<string, string>;\n  try {\n    const raw = (input.headers ?? {}) as Record<string, string>;\n    if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n      throw new HttpFetchValidationError('headers must be an object', 'INVALID_HEADERS');\n    }\n    headers = validateHeaders(raw);\n  } catch (e) {\n    const err = e as HttpFetchValidationError;\n    return {\n      success: false,\n      status: 0,\n      statusText: '',\n      headers: {},\n      body: '',\n      bodyTruncated: false,\n      bytesRead: 0,\n      durationMs: Date.now() - startedAt,\n      url,\n      method,\n      error: err.message,\n      errorCode: err.code ?? 'VALIDATION_ERROR',\n    };","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts#L181-L217","documentation":"Before per-header validation, the http_fetch handler requires input.headers to be a plain object: typeof 'object', non-null, and not an array. Anything else — a string like 'Content-Type: application/json', an array of [name, value] pairs, or a number — throws HttpFetchValidationError 'headers must be an object' (code INVALID_HEADERS), returned as success:false with status 0. headers is optional; omitting it or passing {} is fine because the handler defaults null/undefined to {}.","triggerScenarios":"Calling http_fetch with headers as a raw string (copy-pasted curl -H syntax), an array of tuples like [['accept','json']], or a JSON.stringify'd object that was never parsed. Only non-object values that are present trip the check, since `input.headers ?? {}` absorbs null/undefined.","commonSituations":"Translating curl commands into tool arguments; passing headers: JSON.stringify(obj) by mistake; config systems that deliver arrays of {name, value} records; LLM-generated arguments using the wrong shape.","solutions":["Pass headers as a flat string-to-string object, e.g. { 'content-type': 'application/json' }, or omit the parameter entirely","Convert tuple arrays first: Object.fromEntries([['accept','json'], ['x-trace','abc']])","Parse serialized configs before the call: JSON.parse(headerJson)","Log the runtime type at the call site (console.dir(input.headers)) to confirm what you are actually sending"],"exampleFix":"// before\nawait mcp.callTool('http_fetch', {\n  url: 'https://api.example.com',\n  headers: 'Content-Type: application/json', // INVALID_HEADERS: string, not object\n});\n\n// after\nawait mcp.callTool('http_fetch', {\n  url: 'https://api.example.com',\n  headers: { 'Content-Type': 'application/json' },\n});","handlingStrategy":"type-guard","validationCode":"function normalizeHeaders(input: unknown): Record<string, string> | undefined {\n  if (input == null) return undefined;\n  if (typeof input !== 'object' || Array.isArray(input)) {\n    throw new TypeError('headers must be a flat object of string -> string');\n  }\n  return Object.fromEntries(Object.entries(input).map(([k, v]) => [k, String(v)]));\n}\n// const headers = normalizeHeaders(args.headers);","typeGuard":"function isPlainHeadersObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n// if (args.headers !== undefined && !isPlainHeadersObject(args.headers)) fail fast with your own message","tryCatchPattern":"try {\n  const res = await httpFetch({ url, headers: raw });\n  if (!res.success && res.errorCode === 'INVALID_HEADERS') {\n    // log the runtime type of raw (typeof + Array.isArray) and fix the caller's shape\n  }\n} catch (e) {\n  if (e instanceof HttpFetchValidationError && e.code === 'INVALID_HEADERS') { /* shape error: never retry as-is */ }\n}","preventionTips":["Always build headers as an object literal or Object.fromEntries, never a string or tuple array","Parse serialized header configs (JSON.parse) before passing them","Validate externally sourced arguments with a schema (Zod record of strings) before invoking MCP tools","Remember headers is optional — omit it rather than passing null"],"tags":["http","mcp","headers","type-validation","json"],"backgroundTag":"schema-validation-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}