nocobase/nocobase · error · Error

Kimi extract failed for "${sourceFile.filename}": ${message}

Error message

Kimi extract failed for "${sourceFile.filename}": ${message}

What it means

After uploading a file to Kimi, parseByApi calls client.files.content(uploadedFileId) to retrieve the extracted text. If that call rejects, formatApiError normalizes the underlying error message (error.error.message, response.data..., error.message) into 'Kimi file extraction failed for "<filename>": <message>'. This wraps any network/HTTP/auth failure of the extraction step.

Source

Thrown at packages/plugins/@nocobase/plugin-ai/src/server/llm-providers/kimi/document-loader.ts:79

        uploaded = await this.client.files.create({
          file: createReadStream(tempFilePath),
          // @ts-ignore
          purpose: 'file-extract',
        });
      } catch (error) {
        throw new Error(this.formatApiError(error, sourceFile, 'upload'));
      }

      uploadedFileId = uploaded?.id;
      if (!uploadedFileId) {
        throw new Error('Kimi files.create response missing id');
      }

      let parsedResponse: any;
      try {
        parsedResponse = await this.client.files.content(uploadedFileId);
      } catch (error) {
        throw new Error(this.formatApiError(error, sourceFile, 'extract'));
      }
      if (typeof parsedResponse?.text === 'function') {
        return await parsedResponse.text();
      }
      return typeof parsedResponse === 'string' ? parsedResponse : String(parsedResponse ?? '');
    } finally {
      await fs.rm(tempFilePath, { force: true });
      if (uploadedFileId) {
        await this.deleteRemoteFile(uploadedFileId);
      }
    }
  }

  private async deleteRemoteFile(fileId: string) {
    try {
      await this.client.files.delete(fileId);
    } catch (error) {
      // Ignore cleanup errors to avoid blocking main parsing flow.

View on GitHub (pinned to fa42722fef)

Solutions

  1. Read the wrapped <message> for the real cause (401 → fix API key; 404 → check baseURL consistency; 429 → back off and retry).
  2. Confirm apiKey is valid and active for api.moonshot.cn, and that baseURL (if overridden) points to the same service the file was uploaded to.
  3. Check the uploaded file's format/size is supported by Kimi file-extract.
  4. Retry with backoff for 429/5xx; check network/proxy connectivity to api.moonshot.cn.

Example fix

// before
parsedResponse = await this.client.files.content(uploadedFileId);
// after (retry transient failures)
for (let i = 0; i < 3; i++) {
  try { parsedResponse = await this.client.files.content(uploadedFileId); break; }
  catch (e) { if (i === 2) throw e; await new Promise((r) => setTimeout(r, 1000 * 2 ** i)); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: key + connectivity check before parsing files
const res = await fetch(`${baseURL}/models`, { headers: { Authorization: `Bearer ${apiKey}` } });
if (!res.ok) throw new Error(`Kimi unreachable/unauthorized: ${res.status}`);

Try / catch

try {
  const docs = await loader.load(file);
} catch (err) {
  if (/Kimi file extraction failed/.test(err.message)) {
    const cause = err.message.split(': ').slice(1).join(': ');
    logger.warn(`Kimi extract failed for ${file.filename}: ${cause}`);
    // 429/5xx -> retry with backoff; 401 -> surface credential error to admin
  } else throw err;
}

Prevention

When it happens

Trigger: client.files.content(uploadedFileId) throws: 401 invalid API key, 404 file not found (file expired or wrong baseURL routing to a different backend than the upload), 429 rate limit, 5xx from Moonshot, or network timeout/DNS failure while fetching content.

Common situations: Expired or revoked Kimi API key; baseURL mismatch so the content lookup hits a different service; uploading a file type Moonshot rejects for extraction; hitting Moonshot rate limits during bulk document parsing.

Related errors


AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01). Data as JSON: /api/errors/7975806bf6eb5425. Report an issue: GitHub.