{"record":{"id":"a62febe23d55c24e","repo":"can1357/oh-my-pi","slug":"archive-file-path-is-required","errorCode":null,"errorMessage":"Archive file path is required","messagePattern":"Archive file path is required","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/reader.ts","lineNumber":130,"sourceCode":"\t\t\t\tname: nextSegment,\n\t\t\t\tpath: childPath,\n\t\t\t\tisDirectory,\n\t\t\t\tsize: isDirectory ? 0 : (childEntry?.size ?? entry.size),\n\t\t\t\tmtimeMs: childEntry?.mtimeMs ?? entry.mtimeMs,\n\t\t\t\tmode: childEntry?.mode ?? entry.mode,\n\t\t\t});\n\t\t}\n\n\t\treturn [...children.values()].sort((left, right) =>\n\t\t\tleft.name.toLowerCase().localeCompare(right.name.toLowerCase()),\n\t\t);\n\t}\n\n\t/** Extract one file member's bytes, following symlink aliases. */\n\tasync readFile(subPath: string): Promise<ExtractedArchiveFile> {\n\t\tconst normalizedPath = normalizeArchiveLookupPath(subPath);\n\t\tif (!normalizedPath) {\n\t\t\tthrow new ArchiveError(\"Archive file path is required\");\n\t\t}\n\n\t\tconst resolvedPath = resolveArchiveLinkPath(this.#entries, normalizedPath, this.limits.maxLinkDepth);\n\t\tif (resolvedPath === \"\") {\n\t\t\tthrow new ArchiveError(`Archive path '${normalizedPath}' is a directory`);\n\t\t}\n\t\tconst entry = this.#entries.get(resolvedPath);\n\t\tif (!entry) {\n\t\t\tthrow new ArchiveError(`Archive file '${normalizedPath}' not found`);\n\t\t}\n\t\tif (entry.isDirectory) {\n\t\t\tthrow new ArchiveError(`Archive path '${normalizedPath}' is a directory`);\n\t\t}\n\t\tif (!entry.storage) {\n\t\t\tthrow new ArchiveError(`Archive file '${normalizedPath}' has no readable storage`);\n\t\t}\n\t\tassertArchiveMemberSize(entry.size, normalizedPath, this.limits);\n","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/reader.ts#L112-L148","documentation":"`readFile` requires a non-empty subPath; `normalizeArchiveLookupPath(subPath)` produced an empty/undefined result, so the method throws ArchiveError('Archive file path is required') before doing any lookup. This protects against silently treating an empty path as a root-like lookup (packages/utils/src/ar/reader.ts:130).","triggerScenarios":"Calling `reader.readFile('')`, `readFile(undefined as any)` where an optional path was intended for listDirectory, or a path that normalizes to empty (e.g. '.' or '/' depending on normalization).","commonSituations":"Config value or CLI flag left empty; template-built path where the variable was unset; copy-paste of listDirectory(undefined) usage into readFile; paths consisting only of separators/dots that normalize away.","solutions":["Pass the concrete file path stored in the archive, e.g. readFile('docs/report.pdf').","Validate the path is a non-empty string before calling: `if (!subPath) throw ...`.","If you meant to list the archive root, use listDirectory() (with no argument) instead of readFile('').","Trace where the empty value comes from (unset config/CLI arg) and add an upstream default or required-arg check."],"exampleFix":"// before\nconst path = process.env.REPORT_ENTRY ?? ''; \nconst file = await reader.readFile(path); // throws when unset\n\n// after\nconst path = process.env.REPORT_ENTRY;\nif (!path) throw new Error('REPORT_ENTRY must name a file inside the archive');\nconst file = await reader.readFile(path);","handlingStrategy":"validation","validationCode":"// Require a concrete file path before calling readFile\nfunction requireFilePath(p: string | undefined): string {\n  if (typeof p !== 'string' || p.trim() === '' || p === '.' || p === '/') {\n    throw new Error('A file path inside the archive is required');\n  }\n  return p;\n}\nawait reader.readFile(requireFilePath(configEntryPath));","typeGuard":"function isNonEmptyPath(p: unknown): p is string {\n  return typeof p === 'string' && p.trim().length > 0;\n}","tryCatchPattern":"try {\n  return await reader.readFile(p);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message === 'Archive file path is required') {\n    throw new Error('No archive entry configured — set the file path (use listDirectory() for browsing)');\n  }\n  throw err;\n}","preventionTips":["Make file paths required config/CLI values validated at startup.","Use listDirectory() (no argument) to browse the root — never readFile('').","Check that interpolated path variables are actually set before building calls.","Reject paths that normalize to empty ('.', '/') at the input boundary."],"tags":["archive","missing-argument","validation"],"backgroundTag":"missing-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}