Skip to content
Astera Technology

Why did a valid later parent fail a one-pass load?

Allocate all supplied records before linking their references in one transaction.

Download complete case notes and answer keys ↓

Read the source and its declared rule

The original node batch lists A→B then rootB under an immediate self-FK. B exists in the complete input but not yet in the target whenA is first written. The approved two-pass load allocates nullable-link rows, then updates references and commits only the completed transaction.

Filled synthetic input
RecordInput JSON
Base input{"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]}
Download editable CSV ↓

Compare the actual paths

Correct target contains A→B and rootB. If B is absent from the batch, the later UPDATE actually fails 787 and rollback removes the provisional allocations too.

Misleading and corrected outputs
PathOutput JSON
Misleading path{"error":{"code":787},"nodes":[]}
Corrected path{"error":null,"nodes":[{"id":"A","parent_id":"B"},{"id":"B","parent_id":null}]}
Download editable CSV ↓
Local Node.js program: 22.23.3 or 24.14.1
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};

import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
    const db=new DatabaseSync(':memory:');try{db.exec('PRAGMA foreign_keys=ON;CREATE TABLE node(id TEXT PRIMARY KEY,parent_id TEXT REFERENCES node(id));BEGIN;');
      const error=failed(()=>{for(const n of i.nodes)db.prepare('INSERT INTO node VALUES(?,?)').run(n.id,corrected?null:n.parent);
        if(corrected)for(const n of i.nodes)db.prepare('UPDATE node SET parent_id=? WHERE id=?').run(n.parent,n.id);
        db.exec('COMMIT;');});
      if(error)db.exec('ROLLBACK;');return {error,nodes:rows(db,'SELECT * FROM node ORDER BY id')};
    }finally{db.close();}
  };
const cases=[{"label":"misleading base","input":{"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]},"expected":{"error":{"code":787},"nodes":[]},"corrected":false},{"label":"corrected base","input":{"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]},"expected":{"error":null,"nodes":[{"id":"A","parent_id":"B"},{"id":"B","parent_id":null}]},"corrected":true},{"label":"missing later reference rolls back allocations","input":{"nodes":[{"id":"A","parent":"B"}]},"expected":{"error":{"code":787},"nodes":[]},"corrected":true},{"label":"independent root","input":{"nodes":[{"id":"A","parent":null}]},"expected":{"error":null,"nodes":[{"id":"A","parent_id":null}]},"corrected":true}];
for(const test of cases){
  const actual=run(test.input,test.corrected);
  assert.deepEqual(actual,test.expected,test.label);
  console.log(test.label,actual);
}
Download runnable example ↓

Check boundaries before the next load

MissingB leaves an empty committed target. An independent rootA commits with its null parent.

Editable boundary records and expected outputs
CaseInput JSONExpected output JSON
Missing later parent{"nodes":[{"id":"A","parent":"B"}]}{"error":{"code":787},"nodes":[]}
Independent root record{"nodes":[{"id":"A","parent":null}]}{"error":null,"nodes":[{"id":"A","parent_id":null}]}
Download editable CSV ↓

Can provisional allocations be called a completed load before references pass?

Show the worked answer

No. In the missingB case the reference update fails and all provisional rows roll back. Complete links before commit.

Related resources