{"record":{"id":"05be1b4e0db2cd29","repo":"abhigyanpatwari/GitNexus","slug":"cannot-safely-encode-csv-string-list-item-json","errorCode":null,"errorMessage":"Cannot safely encode CSV string-list item: ${JSON.stringify(unsafe)}","messagePattern":"Cannot safely encode CSV string-list item: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/lbug/csv-generator.ts","lineNumber":142,"sourceCode":"\n/**\n * A numeric column that may legitimately have NO value.\n *\n * `escapeCSVNumber` substitutes a sentinel (-1) for absence, which is right\n * where every row has a span and wrong where absence is the fact being\n * recorded. An empty field is loaded as NULL by COPY, so the column can say\n * \"there is no line here\" instead of pointing at line -1.\n */\nexport const escapeCSVNullableNumber = (value: unknown): string =>\n  typeof value === 'number' && Number.isFinite(value) ? String(value) : '';\n\nconst formatCSVStringArray = (value: unknown): string => {\n  const items = Array.isArray(value)\n    ? value.filter((item): item is string => typeof item === 'string')\n    : [];\n  const unsafe = items.find((item) => /[,\\[\\]'\"\\n\\r]/.test(item));\n  if (unsafe !== undefined) {\n    throw new Error(`Cannot safely encode CSV string-list item: ${JSON.stringify(unsafe)}`);\n  }\n  return `[${items.join(',')}]`;\n};\n\n// ============================================================================\n// CONTENT EXTRACTION (lazy — reads from disk on demand)\n// ============================================================================\n\nconst BINARY_SAMPLE_CHARS = 1000;\nconst UNICODE_REPLACEMENT_CHAR = 0xfffd;\n\n/**\n * Did this text come from a binary payload? Density of non-printables over the\n * first {@link BINARY_SAMPLE_CHARS} characters, above 10%.\n *\n * U+FFFD counts, and it is the character that matters most here (#2889). Every\n * source file enters the pipeline through a `utf-8` decode — the content cache\n * below reads with `fs.readFile(path, 'utf-8')`, and the parse worker decodes","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/lbug/csv-generator.ts#L124-L160","documentation":"formatCSVStringArray() in gitnexus/src/core/lbug/csv-generator.ts renders a string list as a bracketed, comma-joined cell ([a,b,c]) for LadybugDB's CSV bulk load. The cell format has no quoting/escaping mechanism, so any item containing a comma, bracket, single or double quote, CR, or LF (regex /[,\\[\\]'\"\\n\\r]/) cannot be encoded unambiguously and the generator throws rather than emit a row that would silently corrupt the loaded graph.","triggerScenarios":"A graph property emitted as a string array where some item contains a comma or quote — e.g. symbol/decorator names carried from source (annotations like @Foo(x=\"a,b\"), COBOL copybook names, import lists), file paths containing quotes, or generated code whose string literals become identifiers.","commonSituations":"Indexing languages whose decorator/annotation text is captured verbatim into array properties; parsing generated files with punctuation-heavy identifiers; a new parser or plugin emitting raw source substrings into string arrays.","solutions":["Read the offending item from the message (it is JSON.stringify'd) and locate which file/symbol produced it, usually via the item's text in a grep of the repo","If the file is generated or irrelevant, exclude it with .gitnexusignore and re-run analyze","If a legitimate identifier must be indexed, report it upstream — the parser should sanitize/normalize such strings before they reach the CSV layer rather than users losing the file"],"exampleFix":"// before: parser emits raw annotation text\nprops.decorators = ['@Foo(x=\"a,b\")'];\n// after: strip unsafe punctuation before emit\nprops.decorators = props.decorators.map((d) => d.replace(/[,\\[\\]'\"\\n\\r]/g, ''));","handlingStrategy":"validation","validationCode":"const CSV_UNSAFE = /[,\\[\\]'\"\\n\\r]/;\nfunction sanitizeStringList(items: string[]): string[] {\n  return items.map((item) => CSV_UNSAFE.test(item) ? item.replace(CSV_UNSAFE, ' ') : item);\n}","typeGuard":"function isCsvSafeStringArray(value: unknown): value is string[] {\n  return (\n    Array.isArray(value) &&\n    value.every((item) => typeof item === 'string' && !/[,\\[\\]'\"\\n\\r]/.test(item))\n  );\n}","tryCatchPattern":"try {\n  await emitGraph(graph);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Cannot safely encode CSV string-list item')) {\n    // The JSON.stringify'd item is in the message — locate its source file, exclude or report upstream;\n    // retrying unchanged reproduces the same row.\n  }\n  throw err;\n}","preventionTips":["When writing parsers/plugins, normalize punctuation out of array-valued identifiers before attaching them as graph properties","Prefer structured properties (numbers, ids) over raw source substrings for anything list-shaped","When the error fires, use the printed item text to grep the repo and identify the offending file quickly"],"tags":["csv","serialization","ladybugdb","emit","escaping"],"backgroundTag":"csv-escaping-failure","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}