{"record":{"id":"044f6a06e0c3c07c","repo":"can1357/oh-my-pi","slug":"import-source-is-neither-file-nor-directory-tar","errorCode":null,"errorMessage":"Import source is neither file nor directory: ${target}","messagePattern":"Import source is neither file nor directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/auth-broker-cli.ts","lineNumber":520,"sourceCode":"\t\tconst providerId = CLIPROXY_TYPE_TO_PROVIDER[prefix];\n\t\tif (base.startsWith(`${prefix}-`) || base === prefix) return providerId;\n\t}\n\treturn null;\n}\n\nfunction parseCliProxyExpiry(raw: string | undefined): number | null {\n\tif (!raw) return null;\n\t// CLIProxyAPI writes RFC3339-ish dates. `Date.parse` handles both `Z` and offsets.\n\tconst ms = Date.parse(raw);\n\tif (!Number.isFinite(ms)) return null;\n\treturn ms;\n}\n\nasync function collectImportSources(target: string): Promise<string[]> {\n\tconst stat = await fs.stat(target);\n\tif (stat.isFile()) return [target];\n\tif (!stat.isDirectory()) {\n\t\tthrow new Error(`Import source is neither file nor directory: ${target}`);\n\t}\n\tconst entries = await fs.readdir(target, { withFileTypes: true });\n\tconst files: string[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile()) continue;\n\t\tif (!entry.name.endsWith(\".json\")) continue;\n\t\tfiles.push(path.join(target, entry.name));\n\t}\n\tfiles.sort();\n\treturn files;\n}\n\nasync function loadImportPlan(\n\ttarget: string,\n\toverrideProvider: string | undefined,\n\tincludeDisabled: boolean,\n): Promise<{ entries: ImportPlanEntry[]; skipped: Array<{ file: string; reason: string }> }> {\n\tconst files = await collectImportSources(target);","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/auth-broker-cli.ts#L502-L538","documentation":"`collectImportSources` stats the import target and accepts either a regular file or a directory (from which it collects `*.json` files). If the target exists but is neither — e.g. a fifo, socket, or device — it throws. (A nonexistent path fails earlier with ENOENT from `fs.stat` itself.)","triggerScenarios":"`omp auth-broker import <target>` where `<target>` is a special file (fifo, socket, device) or a path that stat resolves to a non-file, non-directory type.","commonSituations":"Pointing import at a fifo/pipe created for streaming; a dangling or unusual symlink; passing a device path like `/dev/null` as the source.","solutions":["Pass a path to a regular credential JSON file or a directory containing `*.json` files.","Remove or replace the broken symlink with a path to the real file/directory.","Verify the target type with `stat <target>` before importing."],"exampleFix":"// before\nomp auth-broker import /tmp/creds.fifo   // neither file nor directory\n// after\nomp auth-broker import /tmp/credentials.json","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\nconst stat = await fs.stat(target);\nif (!stat.isFile() && !stat.isDirectory()) {\n  throw new Error(`${target} must be a JSON file or a directory of *.json files`);\n}\nawait runImport({ source: target });","typeGuard":null,"tryCatchPattern":"try {\n  await runImport({ source: target });\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Import source is neither file nor directory\")) {\n    console.error(\"Pass a credential JSON file or a directory containing *.json exports.\");\n  } else throw err;\n}","preventionTips":["`stat` the target before importing; avoid fifos/sockets/devices as sources.","Fix or remove dangling symlinks before pointing import at them.","Keep credential exports as regular `.json` files in a dedicated directory."],"tags":["cli","filesystem","input-validation"],"backgroundTag":"invalid-import-source","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}