Summary
mergeApiIntegrationsIntoYaml(existingContent, apiIntegrations) in @deepnote/database-integrations throws Error: Document with errors cannot be stringified when existingContent is a readable but malformed YAML file — e.g. a file containing git merge-conflict markers, or a YAML typo. Instead of regenerating the file, the call crashes.
Because this is the primitive that merges API integrations into the user's local integrations file (its own doc comment says it "mirrors what deepnote integrations pull does to the YAML file"), a deepnote integrations pull run against a locally-corrupted file would fail hard rather than heal it.
Reproduction
Against @deepnote/database-integrations@1.5.0 (node 22, yaml@2.8.3):
const { mergeApiIntegrationsIntoYaml, parseIntegrationsDocument } = require('@deepnote/database-integrations')
const conflict = [
'integrations:',
'<<<<<<< HEAD',
' - id: a',
'=======',
' - id: b',
'>>>>>>> branch',
'',
].join('\n')
mergeApiIntegrationsIntoYaml(conflict, []) // throws
mergeApiIntegrationsIntoYaml('a: b: c\n', []) // throws (plain YAML typo)
mergeApiIntegrationsIntoYaml('integrations: []\n', []) // OK
parseIntegrationsDocument(conflict) // returns a Document with errors.length === 6 (NOT null)
Observed output:
mergeApiIntegrationsIntoYaml(conflict-markers, []) => THREW: Error: Document with errors cannot be stringified
mergeApiIntegrationsIntoYaml("a: b: c", []) => THREW: Error: Document with errors cannot be stringified
mergeApiIntegrationsIntoYaml("integrations: []", []) => OK
parseIntegrationsDocument(conflict) => Document, errors.length=6
Root cause
parseIntegrationsDocument uses yaml.parseDocument(...), which collects parse errors onto doc.errors rather than throwing or returning null; it only returns null for empty/whitespace content:
function parseIntegrationsDocument(content) {
if (!content.trim()) return null
return parseDocument(content, { strict: true, version: '1.2' })
}
mergeApiIntegrationsIntoYaml then does:
const doc = (existingContent != null ? parseIntegrationsDocument(existingContent) : null) ?? createNewDocument()
// ... mergeApiIntegrationsIntoDocument(doc, apiIntegrations) ...
return { content: serializeIntegrationsDocument(doc), /* ... */ }
For malformed input, parseIntegrationsDocument returns a non-null errored Document, so ?? createNewDocument() never fires. serializeIntegrationsDocument is doc.toString(), and yaml's Document.toString() throws Document with errors cannot be stringified when doc.errors.length > 0.
(A related case: a well-formed file whose integrations: value is not a sequence throws InvalidIntegrationsTypeError via getOrCreateIntegrationsFromDocument.)
Impact
- Any consumer that merges into a possibly-corrupt existing file crashes instead of self-healing. The most realistic trigger is a git merge conflict in a version-controlled integrations file (or a manual edit typo) — precisely the situation for a file that lives in a user's repo.
Suggested fix
Treat a malformed/errored existing document as "start fresh" instead of trying to serialize it. In mergeApiIntegrationsIntoYaml, fall back to createNewDocument() when the parsed document is null or has doc.errors.length > 0 (and handle the non-sequence integrations: case). Alternatively, have parseIntegrationsDocument return null on parse errors so the existing ?? createNewDocument() fallback already covers it.
Notes / caveats
- The CLI/extension user-facing impact is inferred from the doc comment on
mergeApiIntegrationsIntoYaml ("mirrors what deepnote integrations pull does to the YAML file"). I verified the library primitive throws; I did not trace the exact pull call path, so it's worth confirming whether pull already wraps this.
Filed with an automated reproduction via Claude Code.
Summary
mergeApiIntegrationsIntoYaml(existingContent, apiIntegrations)in@deepnote/database-integrationsthrowsError: Document with errors cannot be stringifiedwhenexistingContentis a readable but malformed YAML file — e.g. a file containing git merge-conflict markers, or a YAML typo. Instead of regenerating the file, the call crashes.Because this is the primitive that merges API integrations into the user's local integrations file (its own doc comment says it "mirrors what
deepnote integrations pulldoes to the YAML file"), adeepnote integrations pullrun against a locally-corrupted file would fail hard rather than heal it.Reproduction
Against
@deepnote/database-integrations@1.5.0(node 22,yaml@2.8.3):Observed output:
Root cause
parseIntegrationsDocumentusesyaml.parseDocument(...), which collects parse errors ontodoc.errorsrather than throwing or returningnull; it only returnsnullfor empty/whitespace content:mergeApiIntegrationsIntoYamlthen does:For malformed input,
parseIntegrationsDocumentreturns a non-null erroredDocument, so?? createNewDocument()never fires.serializeIntegrationsDocumentisdoc.toString(), andyaml'sDocument.toString()throwsDocument with errors cannot be stringifiedwhendoc.errors.length > 0.(A related case: a well-formed file whose
integrations:value is not a sequence throwsInvalidIntegrationsTypeErrorviagetOrCreateIntegrationsFromDocument.)Impact
Suggested fix
Treat a malformed/errored existing document as "start fresh" instead of trying to serialize it. In
mergeApiIntegrationsIntoYaml, fall back tocreateNewDocument()when the parsed document isnullor hasdoc.errors.length > 0(and handle the non-sequenceintegrations:case). Alternatively, haveparseIntegrationsDocumentreturnnullon parse errors so the existing?? createNewDocument()fallback already covers it.Notes / caveats
mergeApiIntegrationsIntoYaml("mirrors whatdeepnote integrations pulldoes to the YAML file"). I verified the library primitive throws; I did not trace the exactpullcall path, so it's worth confirming whetherpullalready wraps this.Filed with an automated reproduction via Claude Code.