plans/35-resume-import-errors.md
7a98f6662, 2026-09-05. Priority P2; effort M for diagnosis; risk medium.NotFoundError: Failed to execute removeChild on Node. It labels Cloud but references localhost. No original file, application version, stack, browser version, or exact import format is available. On 2026-08-16 the owner requested a source file. The report does not establish that PDF extraction and JSON validation share a cause.Run rtk proxy git diff --stat 7a98f6662..HEAD -- apps/web/src/dialogs/resume apps/web/src/features/resume/import packages/import tests/e2e/specs/json-export-import.spec.ts and compare changed source to these anchors.
apps/web/src/dialogs/resume/import.tsx, detectImportType, sniffs %PDF, ZIP PK, MIME, extensions, and JSON shape. Detection does not itself validate the resume.import.utils.ts, detectJsonImportType, identifies JSON Resume by basics without sections or metadata, and distinguishes v4 by metadata without page.parse-json.ts:8, parseResumeJson, dispatches exactly by selected format:
if (format === "reactive-resume-json") return parseReactiveResumeJSON(text);
if (format === "reactive-resume-v4-json") return parseReactiveResumeV4JSON(text);
return parseJSONResume(text);
packages/import/src/reactive-resume-json.tsx, parseReactiveResumeJSON, parses with resumeDataSchema, normalizes missing built-in layout IDs, and parses again. It does not guarantee arbitrary historical JSON is valid.import.tsx around lines 163–190 sends PDF to AI only when a usable provider exists. Otherwise it calls browser extractPdfLines then domain parseResumeText; a no-readable-text PDF has an explicit likely-scanned-document message. DOCX still requires a provider. Do not investigate current offline PDF import as though AI were always mandatory.apps/web/src/features/resume/import/pdf-text.ts:80 calls the ATS extraction worker and documentToLines. Column-aware joining preserves multi-field headers. Parser output quality and DOM lifecycle errors are different measurements.await importResume({ data }), the dialog shows success, calls closeDialog(), then starts navigation. The catch maps known API errors; finally resets importing state. A removeChild stack must be captured at the actual failing DOM operation before modifying this sequence.apps/web/src/dialogs/resume/import.dialog.test.tsx exercises portal mounting and provider navigation; parse-json.test.ts validates format-specific failure; tests/e2e/specs/json-export-import.spec.ts performs a current JSON export/import round trip.Checks run during planning: rtk proxy pnpm --filter @reactive-resume/import test passed 144 tests in 9 files. rtk proxy pnpm --filter web exec vitest run src/dialogs/resume/import.test.ts src/dialogs/resume/import.dialog.test.tsx src/dialogs/resume/parse-json.test.ts src/features/resume/import/pdf-text.test.ts src/features/resume/preview/preview.browser.test.tsx passed 23 tests in 5 files. These are bounded negative controls, not a reproduction of #2768.
Add tests/e2e/specs/import-reproduction.spec.ts and synthetic fixture builders under tests/e2e/fixtures/. Extend the named importer or dialog test only after its layer is implicated. A focused fix may touch the exact implicated parser, detector, or dialog plus its tests. No renderer replacement, global React DOM patch, schema relaxation, broad dialog refactor, AI-provider configuration change, or blanket catch/suppression of removeChild errors. No real user's resume is required.
Generate current JSON with JSON.stringify(structuredClone(sampleResumeData)); generate malformed JSON with a deliberate syntax error and structurally invalid current JSON with a named missing required field. Reuse a minimal valid v4 and JSON Resume fixture from their package tests, copying the small synthetic object into an E2E fixture helper with provenance comments. Create text PDF bytes using @react-pdf/renderer and standard Helvetica containing a name, experience heading, company/role/date, and two bullets. Also generate a blank PDF as the no-text control. Keep PDF bytes generated by code, not downloaded from a private report.
Build a matrix of selected format versus detected format, correct MIME versus empty MIME, and plain-text PDF with no AI provider. Mismatched-format failures should produce an actionable error and preserve the chosen file. A strict MIME failure despite content detection may be a new reproducible case; do not call it the original DOM crash.
Gate: rtk proxy pnpm --filter @reactive-resume/import test remains green, and a new fixture validation test proves each valid fixture parses through its declared parser. Invalid fixtures must fail for the intended reason, not because fixture construction is incomplete.
Use tests/e2e/fixtures/test.ts for disposable authenticated accounts and database cleanup. Attach page.on("pageerror", ...) before opening the dialog and retain full stacks in testInfo.attach. Attach request/response summaries only for exact import endpoints: method, path, status, and timing; never bodies or credentials. Reuse the existing JSON round-trip steps. Wait for either builder URL or an error alert/toast and assert the expected branch. Count newly created synthetic resumes to distinguish server success plus UI failure from server rejection.
Run current JSON, v4 JSON, JSON Resume, text PDF, and blank PDF separately. Repeat successful import with the import response held behind a deterministic route barrier, then release it; exercise close/cancel while pending only through controls the UI actually exposes. Verify that Cancel retains the file when the existing unsaved-file guard rejects closing. Do not introduce arbitrary sleeps or manually remove DOM nodes.
Gate: rtk proxy pnpm build exits 0; with an operator-provided dedicated E2E database in .env.local, run rtk proxy dotenvx run -f .env.local -- pnpm exec playwright test tests/e2e/specs/import-reproduction.spec.ts tests/e2e/specs/json-export-import.spec.ts --project=chromium. Expected: valid inputs create one resume and reach its builder, invalid inputs create none and stay in the dialog, no unhandled page errors. If a case fails, preserve trace/screenshot and full stack under Playwright's artifact directory.
Record each case in a repository diagnostic Markdown file under docs/agents/ only if the operator approves that documentation location, otherwise append results to this plan. Required columns: revision/browser, fixture source, detected/selected format, provider state, API result, UI result, DOM stack, reproduction frequency. If all controls pass, leave the issue awaiting a sanitized original sample and exact stack; do not invent a parser patch.
If a parser fails, write a package unit regression using the smallest synthetic input. If validation fails only with empty MIME, test the detector and form's acceptance policy independently before changing accepted formats. If removeChild reproduces, identify which library owns the parent/child nodes and whether close/navigation causes two owners to remove the same node. A passing parser test cannot validate a DOM lifecycle fix.
Gate: At least one deterministic failing test exists at the implicated layer before code changes. Its captured error must match the proposed root cause. If only a third-party extension reproduces the DOM error, stop and report the extension dependency instead of patching React globally.
Preserve explicit selected-format parsing, schema validation, file retention after failure, duplicate-submit prevention, and single successful navigation. Extend import.dialog.test.tsx for any lifecycle change, using its store-controlled portal harness. If navigation code changes, load the installed TanStack Router lifecycle skill through the root intent command first. For parser changes, extend the corresponding package test and retain valid historical migration cases. Do not swallow unexpected errors or fabricate successful imports.
Gate: The exact failing case passes, all baseline commands above pass, and rtk proxy pnpm --filter web typecheck, rtk proxy pnpm exec turbo boundaries, and rtk proxy git diff --check exit 0. Repeat the deterministic reproduction at least three times without retries masking failures.
/tmp prerequisites enter tests or plans.Stop if original evidence is necessary and absent, provider use would incur unapproved external requests, a schema change would silently discard data, source drift changes the flow, or two fixes fail the same gate. Future work belongs in codex/issue-2768-import-repro; no issue comments, pushes, or merges without later authorization. The coordinator maintains the index.