Skip to content

AssetService.createFromFileStream() fails without RequestContext because of missing languageCode #4651

@Draykee

Description

@Draykee

Describe the bug

Since v3.6, AssetService.createFromFileStream() fails with a NOT NULL constraint violation on asset_translation.languageCode when called without an explicit RequestContext. The method internally calls createAssetInternal() without passing a translations parameter. The fallback in createAssetInternal creates a default translation using ctx.languageCode, but when no RequestContext is provided, RequestContext.empty() is used, which has languageCode: undefined because it creates new Channel() with no defaultLanguageCode.

To Reproduce

// In a plugin service (e.g., generating a sitemap file as an asset)
const stream = new Readable({ encoding: 'utf-8' });
stream.push('sitemap content');
stream.push(null);

// This worked pre-v3.6, fails in v3.6+ with NOT NULL constraint violation
const asset = await this.assetService.createFromFileStream(stream, 'sitemap.txt');

Error output

[TypeORM] Query error: INSERT INTO "asset_translation"("createdAt", "updatedAt", "languageCode", "name", "baseId", "customFields__fix_relational_custom_fields__")
VALUES (DEFAULT, DEFAULT, DEFAULT, $1, $2, DEFAULT) RETURNING "createdAt", "updatedAt", "id"
-- PARAMETERS: ["sitemap.txt",34130]

{
  "severity": "ERROR",
  "code": "23502",
  "detail": "Failing row contains (2026-04-14 16:57:08.266205, 2026-04-14 16:57:08.266205, null, sitemap.txt, 27762, 34130, null).",
  "schema": "public",
  "table": "asset_translation",
  "column": "languageCode"
}

Note that languageCode is null in the failing row and DEFAULT in the query — the value is never set.

Expected behavior

createFromFileStream should work without requiring an explicit RequestContext, as it did before v3.6. The method should either:

  1. Accept an optional translations parameter and forward it to createAssetInternal, or
  2. Use a sensible default languageCode (e.g., LanguageCode.en) when ctx.languageCode is undefined, or
  3. Have RequestContext.empty() provide a fallback languageCode

Root cause analysis

In asset.service.ts:


All overloads of createFromFileStream have an optional RequestContext parameter.

// Line ~380 - createFromFileStream calls createAssetInternal WITHOUT translations
const result = await this.createAssetInternal(ctx, stream, filename, mimetype);
//                                                                    ^ can be undefined -> used empty without languageCode

When translations is undefined, createAssetInternal falls back to:

// Lines ~483-491
assetTranslations = [
    new AssetTranslation({
        languageCode: ctx.languageCode, // undefined when ctx = RequestContext.empty()
        name: defaultName,
        base: savedAsset,
    }),
];

And RequestContext.empty() creates a context with new Channel() which has no defaultLanguageCode, so ctx.languageCode is undefined.

static empty(): RequestContext {
return new RequestContext({
apiType: 'admin',
authorizedAsOwnerOnly: false,
channel: new Channel(),
isAuthorized: true,
});
}

Environment

  • Vendure version: 3.6.2
  • Database: PostgreSQL

Workaround

Pass an explicit RequestContext with a valid languageCode:

const ctx = await this.requestContextService.create({
    apiType: 'admin',
    languageCode: LanguageCode.en,
});
const asset = await this.assetService.createFromFileStream(stream, 'sitemap.txt', ctx);

... or just use any other valid RequestContext

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions