{"record":{"id":"56bb05e0f65ec175","repo":"can1357/oh-my-pi","slug":"name-exceeds-the-sharpshooter-max-file-lines","errorCode":null,"errorMessage":"${name} exceeds the ${SHARPSHOOTER_MAX_FILE_LINES}-line limit","messagePattern":"(.+?) exceeds the (.+?)-line limit","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/sharpshooter/consolidate.ts","lineNumber":262,"sourceCode":"\tconst files: ReplacementFile[] = [];\n\tfor (const item of args.files) {\n\t\tif (!item || typeof item !== \"object\" || !(\"name\" in item) || !(\"content\" in item)) {\n\t\t\tthrow new Error(\"replace_memory_files contains an invalid file entry\");\n\t\t}\n\t\tconst name = item.name;\n\t\tconst rawContent = item.content;\n\t\tif (!isMemoryFileName(name) || typeof rawContent !== \"string\") {\n\t\t\tthrow new Error(\"replace_memory_files contains an invalid file entry\");\n\t\t}\n\t\tif (seen.has(name)) throw new Error(`replace_memory_files contains duplicate ${name}`);\n\t\tseen.add(name);\n\t\tconst redacted = redactSecrets(rawContent);\n\t\tlet lines = redacted.length > 0 ? 1 : 0;\n\t\tfor (let index = 0; index + 1 < redacted.length; index++) {\n\t\t\tif (redacted.charCodeAt(index) === 10) lines += 1;\n\t\t}\n\t\tif (lines > SHARPSHOOTER_MAX_FILE_LINES) {\n\t\t\tthrow new Error(`${name} exceeds the ${SHARPSHOOTER_MAX_FILE_LINES}-line limit`);\n\t\t}\n\t\tfiles.push({ name, content: redacted });\n\t}\n\tconst totalChars = files.reduce((sum, file) => sum + file.content.trim().length, 0);\n\tif (totalChars === 0 && SHARPSHOOTER_MEMORY_FILES.some(name => currentFiles[name].trim().length > 0)) {\n\t\tthrow new Error(\"replace_memory_files returned all-empty content; refusing to wipe memory files\");\n\t}\n\treturn files;\n}\n\nfunction isMemoryFileName(value: unknown): value is SharpshooterMemoryFile {\n\treturn typeof value === \"string\" && (SHARPSHOOTER_MEMORY_FILES as readonly string[]).includes(value);\n}\n\nasync function applyReplacementFiles(bankDir: string, files: readonly ReplacementFile[]): Promise<void> {\n\tconst staged = files.map(file => ({\n\t\t...file,\n\t\ttempPath: path.join(bankDir, `.${file.name}.${process.pid}.${crypto.randomUUID()}.tmp`),","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/sharpshooter/consolidate.ts#L244-L280","documentation":"Each replacement file's content, after secret redaction, is line-counted (newlines counted by scanning char codes). If a file exceeds SHARPSHOOTER_MAX_FILE_LINES lines, this error aborts consolidation so memory files stay small and useful.","triggerScenarios":"A model writes an overly verbose memory file whose redacted content contains more than SHARPSHOOTER_MAX_FILE_LINES newlines.","commonSituations":"Models pasting whole conversation logs into memory files; accumulating changelogs instead of summaries; low-effort models ignoring the length instruction in the tool description.","solutions":["Condense the file content to a terse summary under the line limit.","Split content across the other allowed memory files where appropriate.","Move detail out to project files and keep only pointers in memory.","Retry with a stronger model or prompt emphasizing the per-file line budget."],"exampleFix":"// before: 400-line dump\ncontent: <full session log>\n// after: compact summary\ncontent: \"- auth: uses JWT in src/auth.ts\\n- db: sqlite via bun:sqlite\"","handlingStrategy":"validation","validationCode":"const lineCount = (s: string) => (s.length ? 1 + [...s].filter(c => c === \"\\n\").length : 0);\nfiles.forEach(f => { if (lineCount(f.content) > MAX_LINES) throw new Error(`${f.name} too long`); });","typeGuard":"null","tryCatchPattern":"try {\n  files = parseReplacementFiles(call, currentFiles);\n} catch (err) {\n  if (String(err).includes(\"-line limit\")) {\n    // retry with instruction to condense\n  }\n}","preventionTips":["State the exact line budget in the tool description and prompt.","Summarize rather than transcribe session content into memory files.","Periodically prune memory files during consolidation."],"tags":["validation","limits","size-limit"],"backgroundTag":"size-limit-exceeded","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}