mattermost-community/focalboard · error

invalid board block

Error message

invalid board block

What it means

ErrInvalidBoardBlock signals that a block payload being imported does not represent a valid board. It is declared as a sentinel error in server/model/properties.go and wrapped by ImportBoardJSONL when the JSONL archive lacks a board block. The library throws it to abort imports early instead of persisting a malformed archive.

Source

Thrown at server/model/properties.go:17

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

//go:generate mockgen -destination=mocks/propValueResolverMock.go -package mocks . PropValueResolver

package model

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/mattermost/focalboard/server/utils"
)

var ErrInvalidBoardBlock = errors.New("invalid board block")
var ErrInvalidPropSchema = errors.New("invalid property schema")
var ErrInvalidProperty = errors.New("invalid property")
var ErrInvalidPropertyValue = errors.New("invalid property value")
var ErrInvalidPropertyValueType = errors.New("invalid property value type")
var ErrInvalidDate = errors.New("invalid date property")

// PropValueResolver allows PropDef.GetValue to further decode property values, such as
// looking up usernames from ids.
type PropValueResolver interface {
	GetUserByID(userID string) (*User, error)
}

// BlockProperties is a map of Prop's keyed by property id.
type BlockProperties map[string]BlockProp

// BlockProp represent a property attached to a block (typically a card).
type BlockProp struct {
	ID    string `json:"id"`

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Re-export the board from Focalboard so the archive contains a valid board block as its first/required entry.
  2. Inspect the JSONL file and confirm a line with type "board" exists and is valid JSON.
  3. Do not hand-edit export archives; if edits are needed, validate the JSON afterwards with a schema/linter.
  4. Check that you are importing an archive of the matching format/version for your Focalboard release.

Example fix

// before: importing a truncated export
blocks, err := app.ImportBoardJSONL(teamID, modifiedBy, r.Body)
// after: validate the archive contains a board line first
if !strings.Contains(archiveContent, "\"type\":\"board\"") {
    return errors.New("archive does not contain a board block")
}
blocks, err := app.ImportBoardJSONL(teamID, modifiedBy, r.Body)
Defensive patterns

Strategy: validation

Validate before calling

// check archive has a board block before import
lines := strings.Split(archiveContent, "\n")
hasBoard := false
for _, l := range lines {
    var b map[string]interface{}
    if json.Unmarshal([]byte(l), &b) == nil && b["type"] == "board" {
        hasBoard = true
        break
    }
}
if !hasBoard {
    return errors.New("archive contains no board block")
}

Type guard

func isValidBoardBlock(raw []byte) bool {
    var b struct{ Type string `json:"type"` }
    return json.Unmarshal(raw, &b) == nil && b.Type == "board"
}

Try / catch

blocks, err := app.ImportBoardJSONL(teamID, userID, r.Body)
if errors.Is(err, model.ErrInvalidBoardBlock) {
    http.Error(w, "archive is missing a valid board block", http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: Calling ImportBoardJSONL with an archive whose JSONL stream contains no block of type 'board', or where the board entry fails block validation, producing 'missing board in archive: %w' wrapping this sentinel.

Common situations: Importing a hand-edited or truncated export file, importing an archive exported from a different product/version whose schema differs, or uploading an empty/partial JSONL file through the bulk import API.

Related errors


AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30). Data as JSON: /api/errors/b44c43086a2ec5fa. Report an issue: GitHub.